diff --git a/.changeset/browser-uploads.md b/.changeset/browser-uploads.md new file mode 100644 index 00000000..e41ea1e5 --- /dev/null +++ b/.changeset/browser-uploads.md @@ -0,0 +1,16 @@ +--- +"medialit": minor +"@medialit/uploader": minor +"@medialit/react": minor +"@medialit/cli": minor +--- + +Add browser uploads. `@medialit/uploader` uploads files from the browser with the resumable tus protocol, `@medialit/react` adds the `MediaLitUploader` component and `useMediaLitUpload` hook, and `medialit` adds `createSignatureHandler` for the signature route they call. + +`medialit`: `upload()` now sends the file with Node's built-in `FormData`. With Node's `fetch`, it previously did not send the file. It also takes `fileName` and `mimeType` options. + +`@medialit/cli` adds the `medialit` command. `medialit login` signs in with the browser and the same app picker as the MCP server, and works with self-hosted servers. + +`medialit`: `list()` now sends paging and filters in the body, where the API reads them. They were previously ignored. The client also takes an OAuth `accessToken` instead of an API key. + +`@medialit/uploader`: uploads a Node.js file stream, and takes a `mimeType` option. diff --git a/.changeset/integration-tests-synthetic.md b/.changeset/integration-tests-synthetic.md new file mode 100644 index 00000000..32a84aeb --- /dev/null +++ b/.changeset/integration-tests-synthetic.md @@ -0,0 +1,5 @@ +--- +"@medialit/integration-tests": minor +--- + +Publish the REST, MCP and CLI integration tests as `@medialit/integration-tests`, with a `synthetic` command that checks a MediaLit server end to end and can ping a heartbeat monitor. diff --git a/.codelit-platform.json b/.codelit-platform.json new file mode 100644 index 00000000..e57a5ab2 --- /dev/null +++ b/.codelit-platform.json @@ -0,0 +1,21 @@ +{ + "schemaVersion": 2, + "product": { + "name": "medialit", + "slug": "medialit" + }, + "cliVersion": "0.3.0", + "capabilities": [ + "auth", + "mcp", + "observability", + "billing" + ], + "managedFiles": { + ".github/workflows/platform-conformance.yml": "a3cb49b6623c20a528cae4141958a28bdaf5ad4aa01696ce3845895d84f9bb30" + }, + "productOwnedGlobs": [ + "apps/**", + "packages/**" + ] +} diff --git a/.dockerignore b/.dockerignore index 8bd5123d..e1d8c677 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,9 @@ node_modules +**/node_modules +.git +.next +**/.next **/.env **/.env.local dist - +**/dist diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..7872d5f7 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +# Product-owned. Opens one grouped pull request when a CodeLit Platform +# release is published; all @codelitdev packages release under one version. +# The platform conformance workflow gates the pull request. +version: 2 +updates: + - package-ecosystem: "bun" + directory: "/" + schedule: + interval: "daily" + allow: + - dependency-name: "@codelitdev/*" + groups: + codelit-platform: + patterns: + - "@codelitdev/*" diff --git a/.github/workflows/code-quality.yaml b/.github/workflows/code-quality.yaml index 2c6fc152..4a9d6f48 100644 --- a/.github/workflows/code-quality.yaml +++ b/.github/workflows/code-quality.yaml @@ -34,22 +34,19 @@ jobs: - name: Checkout and pull branch run: git checkout "${GITHUB_REF:11}" && git pull - - name: Install pnpm - run: | - npm i pnpm@10.7.1 -g + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.4.1 - name: Install dependencies - run: pnpm install + run: bun install --frozen-lockfile - - name: Run lint - run: pnpm lint - - - name: Run prettier - run: pnpm prettier + - name: Run lint + run: bun run lint - - name: Build dependencies - run: | - pnpm -r build + - name: Run prettier + run: bun run prettier - name: Run test - run: pnpm test + run: bun run test diff --git a/.github/workflows/deploy-docs-to-pages.yml b/.github/workflows/deploy-docs-to-pages.yml index 466a569d..dc04df3b 100644 --- a/.github/workflows/deploy-docs-to-pages.yml +++ b/.github/workflows/deploy-docs-to-pages.yml @@ -31,18 +31,14 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - - name: Detect package manager - id: detect-package-manager - run: | - echo "manager=npm" >> $GITHUB_OUTPUT - echo "command=ci" >> $GITHUB_OUTPUT - echo "runner=npx --no-install" >> $GITHUB_OUTPUT + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.4.1 - name: Setup Node uses: actions/setup-node@v4 with: node-version: "20" - - name: Setup pnpm - uses: pnpm/action-setup@v4 - name: Setup Pages id: pages uses: actions/configure-pages@v5 @@ -65,14 +61,14 @@ jobs: path: | apps/docs/.next/cache # Generate a new cache whenever packages or source files change. - key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} + key: ${{ runner.os }}-nextjs-${{ hashFiles('**/bun.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} # If source files changed but packages didn't, rebuild from a prior cache. restore-keys: | - ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}- + ${{ runner.os }}-nextjs-${{ hashFiles('**/bun.lock') }}- - name: Install dependencies - run: pnpm i --frozen-lockfile + run: bun install --frozen-lockfile - name: Build with Next.js - run: pnpm --filter @medialit/docs build + run: bun --filter @medialit/docs build - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml new file mode 100644 index 00000000..ab9b4335 --- /dev/null +++ b/.github/workflows/integration-tests.yml @@ -0,0 +1,49 @@ +name: Integration tests + +# Boots the API with Postgres, MinIO and Mailpit and runs the REST, MCP and +# CLI integration tests against it. Make "Integration tests / api" a required +# status check on main to gate pull requests. + +on: + pull_request: + push: + branches: [main] + +concurrency: + group: integration-tests-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + api: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + + - uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.4.1 + + # The API makes thumbnails with ffmpeg and converts images with cwebp. + - name: Install ffmpeg and webp + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends ffmpeg webp + + - run: bun install --frozen-lockfile + + - name: Run integration tests against a fresh stack + run: bash packages/integration-tests/scripts/run-with-local-stack.sh + env: + API_LOG: ${{ runner.temp }}/api.log + + - name: Upload the API log + if: failure() + uses: actions/upload-artifact@v4 + with: + name: api-log + path: ${{ runner.temp }}/api.log + if-no-files-found: ignore diff --git a/.github/workflows/platform-conformance.yml b/.github/workflows/platform-conformance.yml new file mode 100644 index 00000000..bdb9f335 --- /dev/null +++ b/.github/workflows/platform-conformance.yml @@ -0,0 +1,29 @@ +name: Platform conformance + +on: + pull_request: + push: + branches: [main] + +permissions: + contents: read + +jobs: + verify: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.4.1 + # `platform-cli create` intentionally does not resolve dependencies. + # Bootstrap the product lockfile on its first run; all subsequent CI + # installs are immutable and use the frozen lockfile below. + - run: bun install + if: ${{ !hashFiles('bun.lock') }} + - run: bun install --frozen-lockfile + # Pinned to the CLI release that wrote this file. `sync` moves it. + - run: bunx @codelitdev/platform-cli@0.3.0 doctor + - run: bun run --filter "*/api" check:drift + # Fails when the API package has no `test:conformance` script. + - run: bun run --filter "*/api" test:conformance diff --git a/.github/workflows/publish-packages.yaml b/.github/workflows/publish-packages.yaml index 48377177..b9e8658b 100644 --- a/.github/workflows/publish-packages.yaml +++ b/.github/workflows/publish-packages.yaml @@ -25,14 +25,15 @@ jobs: with: fetch-depth: 0 - - name: Install pnpm - uses: pnpm/action-setup@v4 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.4.1 - name: Setup Node uses: actions/setup-node@v4 with: node-version: 24 - cache: 'pnpm' registry-url: 'https://registry.npmjs.org' - name: Configure CI Git User @@ -42,7 +43,7 @@ jobs: git remote set-url origin https://x-access-token:${{ secrets.PAT }}@github.com/${{ github.repository }} - name: Install dependencies - run: pnpm install + run: bun install --frozen-lockfile - name: Create and publish versions id: changesets @@ -50,7 +51,7 @@ jobs: with: commit: "chore: update versions" title: "chore: update versions" - publish: pnpm ci:publish + publish: bun run ci:publish env: GITHUB_TOKEN: ${{ secrets.PAT }} diff --git a/.github/workflows/synthetic.yml b/.github/workflows/synthetic.yml new file mode 100644 index 00000000..2347aad8 --- /dev/null +++ b/.github/workflows/synthetic.yml @@ -0,0 +1,51 @@ +name: Synthetic check + +# Checks every 10 minutes that MediaLit can upload, seal, serve and delete a +# file. Set up before it does anything: +# - secret MEDIALIT_SYNTHETIC_APIKEY: the API key of an app used only for +# monitoring +# - secret MEDIALIT_SYNTHETIC_HEARTBEAT_URL: a heartbeat monitor (Better Stack, +# Healthchecks.io) that alerts on /fail and when pings stop +# - variable MEDIALIT_SYNTHETIC_SERVER: defaults to https://api.medialit.cloud +# +# GitHub can delay scheduled runs and disables them in repositories without +# recent activity. The heartbeat alerts you when that happens too, so give +# it a period of about 30 minutes. + +on: + schedule: + - cron: "*/10 * * * *" + workflow_dispatch: + +concurrency: + group: synthetic + cancel-in-progress: false + +permissions: + contents: read + +jobs: + synthetic: + runs-on: ubuntu-latest + timeout-minutes: 5 + env: + MEDIALIT_APIKEY: ${{ secrets.MEDIALIT_SYNTHETIC_APIKEY }} + MEDIALIT_SYNTHETIC_HEARTBEAT_URL: ${{ secrets.MEDIALIT_SYNTHETIC_HEARTBEAT_URL }} + MEDIALIT_SERVER: ${{ vars.MEDIALIT_SYNTHETIC_SERVER || 'https://api.medialit.cloud' }} + steps: + - name: Skip until configured + if: ${{ env.MEDIALIT_APIKEY == '' }} + run: echo "::notice::Set the MEDIALIT_SYNTHETIC_APIKEY secret to enable the synthetic check." + + # Runs the checked-out code, not the npm package, so a compromised + # package can't see the monitoring key. + - uses: actions/checkout@v4 + if: ${{ env.MEDIALIT_APIKEY != '' }} + - uses: oven-sh/setup-bun@v2 + if: ${{ env.MEDIALIT_APIKEY != '' }} + with: + bun-version: 1.4.1 + - name: Run synthetic check + if: ${{ env.MEDIALIT_APIKEY != '' }} + # The synthetic check only uses Node built-ins, so it needs no install. + run: bun packages/integration-tests/src/bin.ts synthetic diff --git a/.gitignore b/.gitignore index 7fb78b64..e00cd8e8 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,6 @@ dist .agent-skills/ .claude/ .agents/ + +# archives +db.archive diff --git a/.migrations/00000-media-by-apikey.js b/.migrations/00000-media-by-apikey.js deleted file mode 100644 index 4d44ac97..00000000 --- a/.migrations/00000-media-by-apikey.js +++ /dev/null @@ -1,10 +0,0 @@ -db.users.find().forEach(async (user) => { - const firstApikey = await db.apikeys.findOne({ userId: user._id }); - if (firstApikey) { - await db.media.updateMany( - { userId: user._id }, - { $set: { apikey: firstApikey.key } }, - ); - console.log(`Updated ${user.email} media`); - } -}); diff --git a/.migrations/00001-add-internal-apikey.js b/.migrations/00001-add-internal-apikey.js deleted file mode 100644 index 8dd1f0a1..00000000 --- a/.migrations/00001-add-internal-apikey.js +++ /dev/null @@ -1,48 +0,0 @@ -const { MongoClient } = require("mongodb"); -const { nanoid } = require("nanoid"); - -if (!process.env.DB_CONNECTION_STRING) { - console.error("DB_CONNECTION_STRING is not set"); - process.exit(1); -} - -if (!process.env.DB_NAME) { - console.error("DB_NAME is not set"); - process.exit(1); -} - -async function updateCollection() { - const uri = process.env.DB_CONNECTION_STRING; - const client = new MongoClient(uri, { - useNewUrlParser: true, - useUnifiedTopology: true, - }); - - try { - await client.connect(); - const apikeyCollection = client - .db(process.env.DB_NAME) - .collection("apikeys"); - const userCollection = client - .db(process.env.DB_NAME) - .collection("users"); - - const cursor = userCollection.find({}); - while (await cursor.hasNext()) { - const doc = await cursor.next(); - const apikey = { - keyId: nanoid(), - name: "internal-apikey", - key: nanoid(), - userId: doc._id, - internal: true, - }; - await apikeyCollection.insertOne(apikey); - console.log(`Added internal apikey for ${doc.email}`); - } - } finally { - await client.close(); - } -} - -updateCollection().catch(console.error); diff --git a/.migrations/00002-set-unique-id-to-apikeys.js b/.migrations/00002-set-unique-id-to-apikeys.js deleted file mode 100644 index 9f57f833..00000000 --- a/.migrations/00002-set-unique-id-to-apikeys.js +++ /dev/null @@ -1,38 +0,0 @@ -const { MongoClient } = require("mongodb"); -const { nanoid } = require("nanoid"); - -if (!process.env.DB_CONNECTION_STRING) { - console.error("DB_CONNECTION_STRING is not set"); - process.exit(1); -} - -if (!process.env.DB_NAME) { - console.error("DB_NAME is not set"); - process.exit(1); -} - -async function updateCollection() { - const uri = process.env.DB_CONNECTION_STRING; - const client = new MongoClient(uri, { - useNewUrlParser: true, - useUnifiedTopology: true, - }); - - try { - await client.connect(); - const collection = client.db(process.env.DB_NAME).collection("apikeys"); // replace with your DB and collection name - - const cursor = collection.find({}); - while (await cursor.hasNext()) { - const doc = await cursor.next(); - await collection.updateOne( - { _id: doc._id }, - { $set: { keyId: nanoid() } }, - ); - } - } finally { - await client.close(); - } -} - -updateCollection().catch(console.error); diff --git a/.migrations/00003-initialize-subscriptions.js b/.migrations/00003-initialize-subscriptions.js deleted file mode 100644 index d5c168e8..00000000 --- a/.migrations/00003-initialize-subscriptions.js +++ /dev/null @@ -1 +0,0 @@ -db.users.updateMany({}, { $set: { subscriptionStatus: "not-subscribed" } }); diff --git a/.migrations/00004-drop-internal-apikey.js b/.migrations/00004-drop-internal-apikey.js deleted file mode 100644 index 71593ceb..00000000 --- a/.migrations/00004-drop-internal-apikey.js +++ /dev/null @@ -1,2 +0,0 @@ -db.apikeys.deleteMany({ name: "internal-apikey", internal: true }); -db.apikeys.updateMany({}, { $unset: { internal: "" } }); diff --git a/.migrations/00005-migrate-to-dual-bucket-architecture.js b/.migrations/00005-migrate-to-dual-bucket-architecture.js deleted file mode 100644 index 91e5fd51..00000000 --- a/.migrations/00005-migrate-to-dual-bucket-architecture.js +++ /dev/null @@ -1,4 +0,0 @@ -db.media.updateMany( - { accessControl: "public-read" }, - { $set: { accessControl: "public" } }, -); diff --git a/.migrations/00006-mark-first-apikeys-default.js b/.migrations/00006-mark-first-apikeys-default.js deleted file mode 100644 index 05406192..00000000 --- a/.migrations/00006-mark-first-apikeys-default.js +++ /dev/null @@ -1,28 +0,0 @@ -// Find all unique userIds that have non-deleted API keys -const userIds = db.apikeys.distinct("userId", { deleted: { $ne: true } }); - -userIds.forEach((userId) => { - // Check if the user already has a default API key - const hasDefault = db.apikeys.findOne({ - userId: userId, - default: true, - deleted: { $ne: true }, - }); - if (hasDefault) { - return; // Already has a default key, skip - } - - // Find the oldest active API key for this user - const oldestKey = db.apikeys - .find({ userId: userId, deleted: { $ne: true } }) - .sort({ createdAt: 1 }) - .limit(1) - .toArray()[0]; - - if (oldestKey) { - db.apikeys.updateOne( - { _id: oldestKey._id }, - { $set: { default: true } }, - ); - } -}); diff --git a/.migrations/package.json b/.migrations/package.json deleted file mode 100644 index b98c8f6a..00000000 --- a/.migrations/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": ".migrations", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "mongodb": "^6.3.0", - "nanoid": "^3.3.2" - } -} diff --git a/.prettierignore b/.prettierignore index eedc7ff3..2ae0aa4c 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,4 +2,7 @@ node_modules dist **/.next apps/docs/.source -apps/docs/out \ No newline at end of file +apps/docs/out +apps/api/src/db/schema/billing.generated.ts +.codelit-platform.json +.github/workflows/platform-conformance.yml diff --git a/AGENTS.md b/AGENTS.md index 710e7f60..8d382c0b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,7 @@ ## Dev Environment -- Use `pnpm` as the package manager -- This is a monorepo — use `pnpm --filter ` for specific packages +- Use `bun` as the package manager (Bun 1.4.1) +- This is a monorepo — use `bun --filter ` for specific packages +- The API owns product behavior. The web app is a thin client over the API +- Postgres is the database (`DATABASE_URL`). Do not add a second data store for the same records +- Local dependencies run from `docker compose -f docker-compose.local.yml up` (Postgres, MinIO, Mailpit). The API and web app run on the host. Do not run that file together with `docker-compose.yml` diff --git a/README.md b/README.md index 15493388..64a8d422 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ MediaLit is a platform for uploading, transforming, and storing files on any S3-compatible storage provider. -Use it as cloud storage for your apps, a personal media drive, or a file system for AI agents. MediaLit provides both a REST API and an MCP server for managing files programmatically. +Use it as cloud storage for your apps, a personal media drive, or a file system for AI agents. MediaLit provides a REST API, an MCP server and a CLI for managing files. ## Managing your files @@ -10,6 +10,10 @@ This repository contains: - The backend API (under `apps/api`) - The frontend (under `apps/web`) +- The docs (under `apps/docs`) +- The Node.js SDK (`medialit`, under `packages/medialit`) +- The browser uploader and React component (`@medialit/uploader` and `@medialit/react`, under `packages/uploader` and `packages/react`) +- The CLI (`@medialit/cli`, under `packages/cli`) ### Starting the API @@ -24,10 +28,10 @@ EMAIL=email@yourdomain.com Then, start the API: ```bash -pnpm --filter @medialit/api dev +bun --filter @medialit/api dev ``` -When the API starts for the very first time, a user with the provided email will be generated, and their subscription will be renewed for 10 years. +When the API starts for the very first time, a user with the provided email will be generated with an active subscription. The user signs in to the dashboard with an email one-time code. Additionally, a default app will be generated for the user and its API key will be printed in the application logs. The log containing the API key will look something like the following: @@ -49,7 +53,7 @@ Use the frontend if you want to: To start the frontend: ```sh -pnpm --filter @medialit/web dev +bun --filter @medialit/web dev ``` Then log in using the same email you provided above while booting up the API. @@ -71,23 +75,44 @@ sudo apt install ffmpeg webp ### Install dependencies ```bash -pnpm install +bun install ``` -### Build packages +Start Postgres, MinIO, and Mailpit, then copy the local environment file: ```bash -pnpm -r build +docker compose -f docker-compose.local.yml up +cp apps/api/.env.example apps/api/.env ``` +`docker-compose.local.yml` is only the local dependencies. The API and web app run on the host. Do not run it at the same time as `docker-compose.yml`; both publish Postgres on port 5433. MinIO is at (console at ) and Mailpit's inbox is at . + +`BETTER_AUTH_SECRET` must be at least 32 bytes (`openssl rand -base64 48`). Apply database migrations before starting the API: + +```bash +bun --filter @medialit/api db:migrate +``` + +That command runs `apps/api/src/db/migrate.ts` against `DATABASE_URL` and exits. The API process only checks that Postgres is reachable. `docker-compose.yml` runs the same command in an `init` service and starts the API after it exits successfully. + +Existing production data can then be copied from Mongo with `MONGO_URL` (or `DB_CONNECTION_STRING`) and `DATABASE_URL` set, then `bun --filter @medialit/scripts import:mongo`. The import checks that its tables already exist and does not migrate. It keeps user ids, public user ids, API key secrets, and media ids. Tus uploads are local and are not imported. + ### Run the service ```bash -pnpm --filter=@medialit/api dev +bun --filter=@medialit/api dev +``` + +### Run the integration tests + +Pull requests must pass the REST, MCP and CLI integration tests, run against a fresh API with Postgres, MinIO and Mailpit. Run the same check locally (it needs Docker, and leaves your dev stack and `apps/api/.env` alone): + +```bash +bun run test:integration:stack ``` ### Publishing a new version ```bash -pnpm exec changeset +bunx changeset ``` diff --git a/apps/api/.env.example b/apps/api/.env.example index 4874ca3a..4b3cc07e 100644 --- a/apps/api/.env.example +++ b/apps/api/.env.example @@ -1,11 +1,49 @@ -# --- OAuth 2.0 (Phase 3.5 — restart-safety hardening) --- -# Signing key for access + refresh tokens. MUST be at least 32 bytes. -# Generate with: openssl rand -base64 48 -# -# For key rotation, set a comma-separated list — the first key signs -# new tokens, all listed keys are accepted for verification. -OAUTH_SIGNING_KEY= - -# Access token lifetime in seconds. Keep short; refresh tokens are used to -# continue sessions and are revoked on logout. -TOKEN_TTL_SECONDS=900 +# Local stack from docker-compose.local.yml. These values are for development only. +DATABASE_URL=postgresql://medialit:medialit@127.0.0.1:5433/medialit +PORT=8000 +PUBLIC_API_URL=http://localhost:8000 +WEB_ORIGIN=http://localhost:3000 +# Comma-separated browser origins allowed to call Better Auth, besides WEB_ORIGIN. +# AUTH_TRUSTED_ORIGINS= +BETTER_AUTH_SECRET=medialit-local-better-auth-secret-32b-min +EMAIL=admin@example.com +TEMP_FILE_DIR_FOR_UPLOADS=/tmp/medialit + +# MinIO. The S3 clients use path-style requests against CLOUD_ENDPOINT. +# CDN_ENDPOINT includes the public bucket because public file URLs are that +# origin plus the object key. +CLOUD_KEY=medialit +CLOUD_SECRET=medialit-secret +CLOUD_BUCKET_NAME=medialit +CLOUD_PUBLIC_BUCKET_NAME=medialit-public +CLOUD_REGION=us-east-1 +CLOUD_ENDPOINT=http://127.0.0.1:9000 +CLOUD_ENDPOINT_PUBLIC=http://127.0.0.1:9000 +CDN_ENDPOINT=http://127.0.0.1:9000/medialit-public + +POSTHOG_API_KEY= +POSTHOG_HOST= + +# oss unlocks every limit and disables checkout. cloud is Basic until Pro is paid. +# MEDIALIT_DEPLOYMENT_MODE=oss +# Cloud Dodo checkout. Pro is $10/month (1000 minor units) or $100/year +# (10000 minor units). All of these are required together. Leave them empty +# to keep checkout off. +# MEDIALIT_DEPLOYMENT_MODE=cloud +# BILLING_CATALOG_REVISION=1 +# BILLING_CURRENCY=USD +# BILLING_PRO_MONTH_AMOUNT_MINOR=1000 +# BILLING_PRO_YEAR_AMOUNT_MINOR=10000 +# DODO_PRO_MONTH_PRODUCT_ID= +# DODO_PRO_YEAR_PRODUCT_ID= +# DODO_PAYMENTS_API_KEY= +# DODO_PAYMENTS_ENVIRONMENT=test_mode +# DODO_PAYMENTS_WEBHOOK_KEY_CURRENT= +# BILLING_DATA_ENCRYPTION_KEY= # base64 of 32 bytes + +# Mailpit. Leave EMAIL_USER empty; Mailpit accepts mail without SMTP auth. +EMAIL_HOST=127.0.0.1 +EMAIL_PORT=1025 +EMAIL_USER= +EMAIL_PASS= +EMAIL_FROM=medialit@localhost.test diff --git a/apps/api/@types/passport-magic-link/index.d.ts b/apps/api/@types/passport-magic-link/index.d.ts deleted file mode 100644 index e9872595..00000000 --- a/apps/api/@types/passport-magic-link/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module "passport-magic-link"; diff --git a/apps/api/AGENTS.md b/apps/api/AGENTS.md index 5be0e951..68582c18 100644 --- a/apps/api/AGENTS.md +++ b/apps/api/AGENTS.md @@ -2,3 +2,4 @@ - Always stick to industry standards and best practices for maintaining the REST API documentation using swagger and openapi. - Stick to OpenAPI >=3.0.3 specification for the API documentation. +- When adding, removing, renaming, or changing an MCP tool under `src/mcp/tools`, update `packages/integration-tests/src/mcp.ts`. Keep its expected tool inventory exact and exercise every tool's inputs, outputs, and behavior, including cleanup for any test data or settings changes. Run it against a live API when credentials and a test account are available with `bun --filter @medialit/integration-tests test:mcp`. diff --git a/apps/api/Dockerfile b/apps/api/Dockerfile index 321f1418..303fc4a6 100644 --- a/apps/api/Dockerfile +++ b/apps/api/Dockerfile @@ -1,69 +1,27 @@ -FROM node:24-slim AS base -ENV PNPM_HOME="/pnpm" -ENV PATH="$PNPM_HOME:$PATH" -RUN corepack enable +FROM oven/bun:1.4.1 WORKDIR /app -# copy pnpm specific files -COPY package.json ./package.json -COPY pnpm-lock.yaml ./pnpm-lock.yaml -COPY pnpm-workspace.yaml ./pnpm-workspace.yaml +RUN apt-get update \ + && apt-get install -y --no-install-recommends ffmpeg webp \ + && rm -rf /var/lib/apt/lists/* -# copy project files -COPY tsconfig.json . +COPY package.json bun.lock ./ +COPY apps/api/package.json ./apps/api/package.json +COPY packages/images/package.json ./packages/images/package.json +COPY packages/thumbnail/package.json ./packages/thumbnail/package.json +COPY packages/models/package.json ./packages/models/package.json +COPY packages/utils/package.json ./packages/utils/package.json + +ENV NODE_ENV=production + +RUN bun install --frozen-lockfile --production --omit=peer --filter @medialit/api + +COPY tsconfig.json ./ COPY apps/api ./apps/api COPY packages/images ./packages/images COPY packages/thumbnail ./packages/thumbnail COPY packages/models ./packages/models COPY packages/utils ./packages/utils -# TODO: figure out why it is not working -# FROM base AS prod-deps -# RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile --ignore-scripts - -FROM base AS build -RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile -RUN pnpm --filter=@medialit/thumbnail build -RUN pnpm --filter=@medialit/images build -RUN pnpm --filter=@medialit/utils build -RUN pnpm --filter=@medialit/models build -RUN pnpm --filter=@medialit/api build - -FROM node:20-slim -ENV PNPM_HOME="/pnpm" -ENV PATH="$PNPM_HOME:$PATH" -RUN corepack enable - -# install required softwares -RUN apt-get update && apt-get -y install ffmpeg webp - -# set environment -ENV NODE_ENV production - -# create app directory -WORKDIR /app - -# copy files -#COPY --chown=node:node --from=prod-deps /app/node_modules /app/node_modules -COPY --chown=node:node --from=build /app/package.json /app/package.json -COPY --chown=node:node --from=build /app/pnpm-lock.yaml /app/pnpm-lock.yaml -COPY --chown=node:node --from=build /app/pnpm-workspace.yaml /app/pnpm-workspace.yaml -COPY --chown=node:node --from=build /app/packages/thumbnail/package.json /app/packages/thumbnail/package.json -COPY --chown=node:node --from=build /app/packages/thumbnail/dist /app/packages/thumbnail/dist -COPY --chown=node:node --from=build /app/packages/thumbnail/bin /app/packages/thumbnail/bin -COPY --chown=node:node --from=build /app/packages/images/package.json /app/packages/images/package.json -COPY --chown=node:node --from=build /app/packages/images/dist /app/packages/images/dist -COPY --chown=node:node --from=build /app/packages/utils/package.json /app/packages/utils/package.json -COPY --chown=node:node --from=build /app/packages/utils/dist /app/packages/utils/dist -COPY --chown=node:node --from=build /app/packages/models/package.json /app/packages/models/package.json -COPY --chown=node:node --from=build /app/packages/models/dist /app/packages/models/dist -COPY --chown=node:node --from=build /app/apps/api/package.json /app/apps/api/package.json -COPY --chown=node:node --from=build /app/apps/api/dist /app/apps/api/dist - -# Run pnpm install -RUN pnpm install --prod --frozen-lockfile --ignore-scripts - -# set a low privileged user -USER node - -CMD ["pnpm", "--filter=@medialit/api", "start"] \ No newline at end of file +USER bun +CMD ["bun", "apps/api/src/index.ts"] diff --git a/apps/api/__tests__/mcp/media-tool.test.ts b/apps/api/__tests__/mcp/media-tool.test.ts index cf54313f..3eefb5da 100644 --- a/apps/api/__tests__/mcp/media-tool.test.ts +++ b/apps/api/__tests__/mcp/media-tool.test.ts @@ -18,11 +18,15 @@ describe("MCP get_total_storage", () => { let queriedUserId: unknown; const response = await handleGetTotalStorageTool( + {}, { authInfo: { - clientId: user.id, - token: "test-api-key", - user, + extra: { + authKind: "apikey", + userId: user._id, + user, + apikey: "test-api-key", + }, }, }, { diff --git a/apps/api/__tests__/mcp/upload-tool.test.ts b/apps/api/__tests__/mcp/upload-tool.test.ts index e174d067..cfd424cd 100644 --- a/apps/api/__tests__/mcp/upload-tool.test.ts +++ b/apps/api/__tests__/mcp/upload-tool.test.ts @@ -16,9 +16,12 @@ import { function authInfo(user: any) { return { authInfo: { - clientId: user.id, - token: "test-api-key", - user, + extra: { + authKind: "apikey", + userId: user.id, + user, + apikey: "test-api-key", + }, }, }; } diff --git a/apps/api/__tests__/mcp/whoami-tool.test.ts b/apps/api/__tests__/mcp/whoami-tool.test.ts new file mode 100644 index 00000000..e9787160 --- /dev/null +++ b/apps/api/__tests__/mcp/whoami-tool.test.ts @@ -0,0 +1,59 @@ +import { Constants } from "@medialit/models"; +import test, { describe } from "node:test"; +import assert from "node:assert"; +import { handleWhoamiTool } from "../../src/mcp/tools/whoami"; +import { maxStorageAllowedSubscribed } from "../../src/config/constants"; + +const user = { + id: "user-1", + _id: "user-1", + email: "hi@example.com", + subscriptionStatus: Constants.SubscriptionStatus.SUBSCRIBED, +}; + +function extra(authKind: "oauth" | "apikey") { + return { + authInfo: { + extra: { authKind, userId: user._id, user, apikey: "app-secret" }, + }, + }; +} + +describe("MCP whoami", () => { + test("reports the account, the connected app and its usage", async () => { + const queried: unknown[] = []; + const response = await handleWhoamiTool({}, extra("oauth"), { + getApiKey: async (key: string) => { + queried.push(key); + return { + key, + keyId: "app-1", + name: "My Store", + default: true, + userId: user._id, + } as any; + }, + getMediaCount: async ({ userId, apikey }: any) => { + queried.push([userId, apikey]); + return 3; + }, + getTotalSpace: async () => 2048, + }); + + assert.deepEqual(queried, ["app-secret", ["user-1", "app-secret"]]); + assert.deepEqual((response as any).structuredContent, { + auth: "oauth", + email: "hi@example.com", + app: { id: "app-1", name: "My Store", default: true }, + files: 3, + storage: 2048, + maxStorage: maxStorageAllowedSubscribed, + }); + assert.doesNotMatch(response.content[0].text, /app-secret/); + }); + + test("rejects calls without auth", async () => { + const response = await handleWhoamiTool({}, {}); + assert.equal((response as any).isError, true); + }); +}); diff --git a/apps/api/__tests__/openapi-paths.test.ts b/apps/api/__tests__/openapi-paths.test.ts new file mode 100644 index 00000000..6762e123 --- /dev/null +++ b/apps/api/__tests__/openapi-paths.test.ts @@ -0,0 +1,24 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import swagger from "../src/swagger_output.json"; + +const expectedPaths = [ + "/health", + "/media/create", + "/media/delete/{mediaId}", + "/media/get", + "/media/get/count", + "/media/get/size", + "/media/get/{mediaId}", + "/media/seal/{mediaId}", + "/media/signature/create", + "/settings/media/create", + "/settings/media/get", +]; + +test("public OpenAPI paths stay the same", () => { + assert.deepEqual( + Object.keys(swagger.paths).sort(), + [...expectedPaths].sort(), + ); +}); diff --git a/apps/api/__tests__/tus/tus-server.test.ts b/apps/api/__tests__/tus/tus-server.test.ts index 5b7c9dc3..6e6021d9 100644 --- a/apps/api/__tests__/tus/tus-server.test.ts +++ b/apps/api/__tests__/tus/tus-server.test.ts @@ -1,7 +1,7 @@ import { Constants } from "@medialit/models"; import test, { afterEach, describe, mock } from "node:test"; import assert from "node:assert"; -import { handleTusUploadCreate } from "../../src/tus/tus-server"; +import { handleTusUploadCreate, uploadGroup } from "../../src/tus/tus-server"; import mediaQueries from "../../src/media/queries"; import { createTusUpload } from "../../src/tus/queries"; import { @@ -78,4 +78,17 @@ describe("TUS upload creation", () => { }, ); }); + + test("keeps the signature's group when the client sends another", () => { + assert.strictEqual( + uploadGroup("workspace-42", "client-forged-group"), + "workspace-42", + ); + }); + + test("uses the client's group when the signature has none", () => { + assert.strictEqual(uploadGroup(undefined, "avatars"), "avatars"); + assert.strictEqual(uploadGroup(null, "avatars"), "avatars"); + assert.strictEqual(uploadGroup(undefined, undefined), undefined); + }); }); diff --git a/apps/api/billing.config.ts b/apps/api/billing.config.ts new file mode 100644 index 00000000..37849da8 --- /dev/null +++ b/apps/api/billing.config.ts @@ -0,0 +1,27 @@ +import { defineBillingConfig } from "@codelitdev/billing/config"; + +// Paid plans only. Basic is the unpaid cloud plan and OSS is a deployment +// mode; neither belongs in planIds. +export default defineBillingConfig({ + dialect: "postgresql", + adapter: "drizzle", + output: "./src/db/schema/billing.generated.ts", + billableEntity: { + modelName: "user", + tableImport: "./auth.generated", + tableExport: "user", + idColumn: "id", + idType: "text", + onDelete: "restrict", + }, + payer: { + modelName: "user", + tableImport: "./billing-payer", + tableExport: "payerUser", + idColumn: "id", + idType: "text", + onDelete: "restrict", + }, + planIds: ["pro"], + requiredOfferKeys: ["pro_month", "pro_year"], +}); diff --git a/apps/api/drizzle.config.ts b/apps/api/drizzle.config.ts new file mode 100644 index 00000000..6bf37d38 --- /dev/null +++ b/apps/api/drizzle.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from "drizzle-kit"; + +export default defineConfig({ + schema: [ + "./src/db/schema/auth.generated.ts", + "./src/db/schema/domain.ts", + "./src/db/schema/billing.generated.ts", + ], + out: "./drizzle", + dialect: "postgresql", + dbCredentials: { + url: process.env.DATABASE_URL || "", + }, +}); diff --git a/apps/api/drizzle/0000_auth.sql b/apps/api/drizzle/0000_auth.sql new file mode 100644 index 00000000..d1ef5f7f --- /dev/null +++ b/apps/api/drizzle/0000_auth.sql @@ -0,0 +1,178 @@ +-- AUTO-GENERATED by scripts/generate-auth-schema.mjs. DO NOT EDIT. +CREATE TABLE IF NOT EXISTS "user" ( + id text PRIMARY KEY, + "name" text NOT NULL, + "email" text NOT NULL UNIQUE, + "email_verified" boolean NOT NULL, + "image" text, + "created_at" timestamptz NOT NULL, + "updated_at" timestamptz NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "session" ( + id text PRIMARY KEY, + "expires_at" timestamptz NOT NULL, + "token" text NOT NULL UNIQUE, + "created_at" timestamptz NOT NULL, + "updated_at" timestamptz NOT NULL, + "ip_address" text, + "user_agent" text, + "user_id" text NOT NULL REFERENCES "user"("id") ON DELETE CASCADE +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "account" ( + id text PRIMARY KEY, + "issuer" text NOT NULL, + "account_id" text NOT NULL, + "provider_id" text NOT NULL, + "user_id" text NOT NULL REFERENCES "user"("id") ON DELETE CASCADE, + "access_token" text, + "refresh_token" text, + "id_token" text, + "access_token_expires_at" timestamptz, + "refresh_token_expires_at" timestamptz, + "scope" text, + "password" text, + "created_at" timestamptz NOT NULL, + "updated_at" timestamptz NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "verification" ( + id text PRIMARY KEY, + "identifier" text NOT NULL, + "value" text NOT NULL, + "expires_at" timestamptz NOT NULL, + "created_at" timestamptz NOT NULL, + "updated_at" timestamptz NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "jwks" ( + id text PRIMARY KEY, + "public_key" text NOT NULL, + "private_key" text NOT NULL, + "created_at" timestamptz NOT NULL, + "expires_at" timestamptz, + "alg" text, + "crv" text +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "oauth_client" ( + id text PRIMARY KEY, + "client_id" text NOT NULL UNIQUE, + "client_secret" text, + "client_discovery_id" text, + "disabled" boolean, + "skip_consent" boolean, + "enable_end_session" boolean, + "subject_type" text, + "scopes" text[], + "client_credentials_scopes" text[], + "user_id" text REFERENCES "user"("id") ON DELETE CASCADE, + "created_at" timestamptz, + "updated_at" timestamptz, + "name" text, + "uri" text, + "icon" text, + "contacts" text[], + "tos" text, + "policy" text, + "software_id" text, + "software_version" text, + "software_statement" text, + "redirect_uris" text[] NOT NULL, + "post_logout_redirect_uris" text[], + "backchannel_logout_uri" text, + "backchannel_logout_session_required" boolean, + "token_endpoint_auth_method" text, + "application_type" text, + "jwks" text, + "jwks_uri" text, + "grant_types" text[], + "response_types" text[], + "require_pkce" boolean, + "dpop_bound_access_tokens" boolean, + "reference_id" text, + "metadata" jsonb +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "oauth_resource" ( + id text PRIMARY KEY, + "identifier" text NOT NULL UNIQUE, + "name" text NOT NULL, + "access_token_ttl" integer, + "refresh_token_ttl" integer, + "signing_algorithm" text, + "signing_key_id" text, + "allowed_scopes" text[], + "custom_claims" jsonb, + "dpop_bound_access_tokens_required" boolean, + "disabled" boolean, + "created_at" timestamptz, + "updated_at" timestamptz, + "policy_version" integer, + "metadata" jsonb +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "oauth_client_resource" ( + id text PRIMARY KEY, + "client_id" text NOT NULL REFERENCES "oauth_client"("client_id") ON DELETE CASCADE, + "resource_id" text NOT NULL REFERENCES "oauth_resource"("identifier") ON DELETE CASCADE, + "metadata" jsonb, + "created_at" timestamptz +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "oauth_refresh_token" ( + id text PRIMARY KEY, + "token" text NOT NULL UNIQUE, + "client_id" text NOT NULL REFERENCES "oauth_client"("client_id") ON DELETE CASCADE, + "session_id" text REFERENCES "session"("id") ON DELETE SET NULL, + "user_id" text NOT NULL REFERENCES "user"("id") ON DELETE CASCADE, + "reference_id" text, + "authorization_code_id" text, + "resources" text[], + "requested_user_info_claims" text[], + "expires_at" timestamptz, + "created_at" timestamptz, + "revoked" timestamptz, + "rotated_at" timestamptz, + "rotation_replay_response" text, + "rotation_replay_expires_at" timestamptz, + "auth_time" timestamptz, + "confirmation" jsonb, + "scopes" text[] NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "oauth_access_token" ( + id text PRIMARY KEY, + "token" text UNIQUE, + "client_id" text NOT NULL REFERENCES "oauth_client"("client_id") ON DELETE CASCADE, + "session_id" text REFERENCES "session"("id") ON DELETE SET NULL, + "user_id" text REFERENCES "user"("id") ON DELETE CASCADE, + "reference_id" text, + "authorization_code_id" text, + "resources" text[], + "requested_user_info_claims" text[], + "refresh_id" text REFERENCES "oauth_refresh_token"("id") ON DELETE CASCADE, + "expires_at" timestamptz, + "created_at" timestamptz, + "revoked" timestamptz, + "confirmation" jsonb, + "scopes" text[] NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "oauth_consent" ( + id text PRIMARY KEY, + "client_id" text NOT NULL REFERENCES "oauth_client"("client_id") ON DELETE CASCADE, + "user_id" text REFERENCES "user"("id") ON DELETE CASCADE, + "reference_id" text, + "resources" text[], + "requested_user_info_claims" text[], + "scopes" text[] NOT NULL, + "created_at" timestamptz, + "updated_at" timestamptz +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "oauth_client_assertion" ( + id text PRIMARY KEY, + "expires_at" timestamptz NOT NULL +); diff --git a/apps/api/drizzle/0001_domain.sql b/apps/api/drizzle/0001_domain.sql new file mode 100644 index 00000000..545c66a7 --- /dev/null +++ b/apps/api/drizzle/0001_domain.sql @@ -0,0 +1,93 @@ +CREATE TABLE IF NOT EXISTS "profiles" ( + "user_id" text PRIMARY KEY REFERENCES "user"("id") ON DELETE CASCADE, + "public_user_id" text NOT NULL UNIQUE, + "active" boolean NOT NULL DEFAULT true, + "customer_id" text UNIQUE, + "subscription_id" text UNIQUE, + "subscription_ends_after" timestamptz, + "subscription_method" text, + "subscription_status" text NOT NULL DEFAULT 'not-subscribed' +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "api_keys" ( + "id" text PRIMARY KEY, + "key_id" text NOT NULL UNIQUE, + "name" text NOT NULL, + "key" text NOT NULL UNIQUE, + "user_id" text NOT NULL REFERENCES "user"("id") ON DELETE CASCADE, + "restriction" text, + "http_referrers" text[] NOT NULL DEFAULT '{}', + "ip_addresses" text[] NOT NULL DEFAULT '{}', + "deleted" boolean NOT NULL DEFAULT false, + "is_default" boolean NOT NULL DEFAULT false, + "created_at" timestamptz NOT NULL, + "updated_at" timestamptz NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "api_keys_user_name_uidx" ON "api_keys" ("user_id", "name") WHERE "deleted" = false; +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "api_keys_one_default_uidx" ON "api_keys" ("user_id") WHERE "is_default" = true AND "deleted" = false; +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "media" ( + "id" text PRIMARY KEY, + "file_name" text NOT NULL, + "media_id" text NOT NULL UNIQUE, + "user_id" text NOT NULL REFERENCES "user"("id") ON DELETE CASCADE, + "apikey" text NOT NULL, + "original_file_name" text NOT NULL, + "mime_type" text NOT NULL, + "size" bigint NOT NULL, + "thumbnail_generated" boolean NOT NULL DEFAULT false, + "access_control" text NOT NULL DEFAULT 'private', + "group_name" text, + "caption" text, + "temp" boolean NOT NULL DEFAULT true, + "created_at" timestamptz NOT NULL, + "updated_at" timestamptz NOT NULL +); +--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "media_owner_idx" ON "media" ("user_id", "apikey", "temp"); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "media_settings" ( + "id" text PRIMARY KEY, + "user_id" text NOT NULL REFERENCES "user"("id") ON DELETE CASCADE, + "apikey" text NOT NULL UNIQUE, + "use_web_p" boolean, + "webp_output_quality" bigint, + "thumbnail_width" bigint, + "thumbnail_height" bigint +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "signatures" ( + "id" text PRIMARY KEY, + "user_id" text NOT NULL, + "apikey" text NOT NULL, + "signature" text NOT NULL UNIQUE, + "valid_till" timestamptz NOT NULL, + "group_name" text, + "created_at" timestamptz NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "tus_uploads" ( + "id" text PRIMARY KEY, + "upload_id" text NOT NULL UNIQUE, + "user_id" text NOT NULL, + "apikey" text NOT NULL, + "upload_length" bigint NOT NULL, + "upload_offset" bigint NOT NULL DEFAULT 0, + "metadata" jsonb NOT NULL, + "temp_file_path" text, + "signature" text, + "is_complete" boolean NOT NULL DEFAULT false, + "expires_at" timestamptz, + "created_at" timestamptz NOT NULL, + "updated_at" timestamptz NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "legacy_revoked_tokens" ( + "jti" text PRIMARY KEY, + "user_id" text NOT NULL, + "client_id" text NOT NULL, + "expires_at" timestamptz NOT NULL, + "revoked_at" timestamptz NOT NULL +); diff --git a/apps/api/drizzle/0002_billing.sql b/apps/api/drizzle/0002_billing.sql new file mode 100644 index 00000000..20fa742d --- /dev/null +++ b/apps/api/drizzle/0002_billing.sql @@ -0,0 +1,252 @@ +CREATE TABLE IF NOT EXISTS "billing_price_entries" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), + "offer_key" text NOT NULL, + "plan" text NOT NULL, + "billing_interval" text NOT NULL, + "currency" text NOT NULL, + "amount_minor" integer NOT NULL, + "provider_trial_days" integer NOT NULL DEFAULT 0, + "provider" text NOT NULL, + "provider_product_id" text NOT NULL, + "verified_at" timestamptz, + "created_at" timestamptz NOT NULL DEFAULT now(), + "updated_at" timestamptz NOT NULL DEFAULT now(), + CONSTRAINT "billing_price_entries_amount_check" CHECK ("amount_minor" > 0), + CONSTRAINT "billing_price_entries_trial_days_check" CHECK ("provider_trial_days" >= 0), + CONSTRAINT "billing_price_entries_currency_check" CHECK ("currency" ~ '^[A-Z]{3}$'), + CONSTRAINT "billing_price_entries_plan_check" CHECK ("plan" IN ('pro')), + CONSTRAINT "billing_price_entries_interval_check" CHECK ("billing_interval" IN ('month', 'year')) +); +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_price_entries_provider_product_uidx" ON "billing_price_entries" ("provider", "provider_product_id"); +--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "billing_price_entries_offer_key_idx" ON "billing_price_entries" ("offer_key"); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "billing_catalog_revisions" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), + "revision" integer NOT NULL UNIQUE, + "checkout_provider" text NOT NULL, + "status" text NOT NULL DEFAULT 'pending_verification', + "verified_at" timestamptz, + "activated_at" timestamptz, + "retired_at" timestamptz, + "created_at" timestamptz NOT NULL DEFAULT now(), + "updated_at" timestamptz NOT NULL DEFAULT now(), + CONSTRAINT "billing_catalog_revisions_status_check" CHECK ("status" IN ('pending_verification', 'active', 'retired', 'invalid', 'abandoned')), + CONSTRAINT "billing_catalog_revisions_revision_check" CHECK ("revision" > 0) +); +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_catalog_revisions_active_provider_uidx" ON "billing_catalog_revisions" ("checkout_provider") WHERE "status" = 'active'; +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "billing_catalog_revision_items" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), + "catalog_revision_id" uuid NOT NULL REFERENCES "billing_catalog_revisions"("id") ON DELETE CASCADE, + "offer_key" text NOT NULL, + "billing_price_entry_id" uuid NOT NULL REFERENCES "billing_price_entries"("id") ON DELETE RESTRICT +); +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_catalog_revision_items_revision_key_uidx" ON "billing_catalog_revision_items" ("catalog_revision_id", "offer_key"); +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_catalog_revision_items_revision_price_uidx" ON "billing_catalog_revision_items" ("catalog_revision_id", "billing_price_entry_id"); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "billing_provider_customers" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), + "provider" text NOT NULL, + "payer_id" text NOT NULL REFERENCES "user"("id") ON DELETE RESTRICT, + "payer_email" text NOT NULL DEFAULT '', + "provider_customer_id" text, + "idempotency_key" text NOT NULL, + "status" text NOT NULL DEFAULT 'creating', + "last_error" text, + "created_at" timestamptz NOT NULL DEFAULT now(), + "updated_at" timestamptz NOT NULL DEFAULT now(), + CONSTRAINT "billing_provider_customers_status_check" CHECK ("status" IN ('creating', 'active', 'conflicted')) +); +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_provider_customers_provider_payer_uidx" ON "billing_provider_customers" ("provider", "payer_id"); +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_provider_customers_provider_customer_uidx" ON "billing_provider_customers" ("provider", "provider_customer_id") WHERE "provider_customer_id" IS NOT NULL; +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_provider_customers_idempotency_uidx" ON "billing_provider_customers" ("idempotency_key"); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "billing_checkout_attempts" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), + "attempt_id" text NOT NULL UNIQUE, + "billable_entity_id" text NOT NULL REFERENCES "user"("id") ON DELETE RESTRICT, + "payer_id" text NOT NULL REFERENCES "user"("id") ON DELETE RESTRICT, + "payer_email" text NOT NULL DEFAULT '', + "return_url" text NOT NULL DEFAULT '', + "provider" text NOT NULL, + "catalog_revision" integer NOT NULL, + "offer_key" text NOT NULL, + "requested_plan" text NOT NULL, + "requested_interval" text NOT NULL, + "billing_price_entry_id" uuid NOT NULL REFERENCES "billing_price_entries"("id") ON DELETE RESTRICT, + "quoted_amount_minor" integer NOT NULL, + "quoted_currency" text NOT NULL, + "billing_customer_id" uuid REFERENCES "billing_provider_customers"("id") ON DELETE RESTRICT, + "provider_checkout_session_id" text, + "checkout_url_encrypted" text, + "idempotency_key" text NOT NULL, + "status" text NOT NULL DEFAULT 'creating', + "expires_at" timestamptz NOT NULL, + "last_error" text, + "completed_at" timestamptz, + "created_at" timestamptz NOT NULL DEFAULT now(), + "updated_at" timestamptz NOT NULL DEFAULT now(), + CONSTRAINT "billing_checkout_attempts_status_check" CHECK ("status" IN ('creating', 'open', 'completed', 'expired', 'abandoned', 'conflicted')), + CONSTRAINT "billing_checkout_attempts_amount_check" CHECK ("quoted_amount_minor" > 0), + CONSTRAINT "billing_checkout_attempts_plan_check" CHECK ("requested_plan" IN ('pro')), + CONSTRAINT "billing_checkout_attempts_interval_check" CHECK ("requested_interval" IN ('month', 'year')) +); +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_checkout_attempts_provider_session_uidx" ON "billing_checkout_attempts" ("provider", "provider_checkout_session_id") WHERE "provider_checkout_session_id" IS NOT NULL; +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_checkout_attempts_idempotency_uidx" ON "billing_checkout_attempts" ("idempotency_key"); +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_checkout_attempts_entity_nonterminal_uidx" ON "billing_checkout_attempts" ("billable_entity_id") WHERE "status" IN ('creating', 'open'); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "billing_subscriptions" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), + "billable_entity_id" text NOT NULL REFERENCES "user"("id") ON DELETE RESTRICT, + "billing_customer_id" uuid NOT NULL REFERENCES "billing_provider_customers"("id") ON DELETE RESTRICT, + "payer_id" text NOT NULL REFERENCES "user"("id") ON DELETE RESTRICT, + "origin_checkout_attempt_id" uuid REFERENCES "billing_checkout_attempts"("id") ON DELETE RESTRICT, + "provider" text NOT NULL, + "provider_subscription_id" text NOT NULL, + "provider_product_id" text NOT NULL, + "billing_price_entry_id" uuid NOT NULL REFERENCES "billing_price_entries"("id") ON DELETE RESTRICT, + "catalog_revision" integer NOT NULL, + "offer_key" text NOT NULL, + "plan" text NOT NULL, + "billing_interval" text NOT NULL, + "status" text NOT NULL DEFAULT 'pending', + "current_period_starts_at" timestamptz, + "current_period_ends_at" timestamptz, + "paid_through_at" timestamptz, + "trial_ends_at" timestamptz, + "cancel_at_period_end" boolean NOT NULL DEFAULT false, + "is_entitlement_source" boolean NOT NULL DEFAULT false, + "provider_occurred_at" timestamptz, + "provider_version" text, + "last_observed_at" timestamptz, + "last_reconciled_at" timestamptz, + "created_at" timestamptz NOT NULL DEFAULT now(), + "updated_at" timestamptz NOT NULL DEFAULT now(), + CONSTRAINT "billing_subscriptions_status_check" CHECK ("status" IN ('pending', 'trialing', 'active', 'past_due', 'cancelled', 'expired')), + CONSTRAINT "billing_subscriptions_plan_check" CHECK ("plan" IN ('pro')), + CONSTRAINT "billing_subscriptions_interval_check" CHECK ("billing_interval" IN ('month', 'year')) +); +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_subscriptions_provider_subscription_uidx" ON "billing_subscriptions" ("provider", "provider_subscription_id"); +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_subscriptions_entity_source_uidx" ON "billing_subscriptions" ("billable_entity_id") WHERE "is_entitlement_source" = true; +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "billing_plan_change_attempts" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), + "change_id" text NOT NULL UNIQUE, + "billable_entity_id" text NOT NULL REFERENCES "user"("id") ON DELETE RESTRICT, + "subscription_id" uuid NOT NULL REFERENCES "billing_subscriptions"("id") ON DELETE RESTRICT, + "actor_id" text NOT NULL, + "payer_id" text NOT NULL REFERENCES "user"("id") ON DELETE RESTRICT, + "provider" text NOT NULL, + "idempotency_key" text NOT NULL, + "current_catalog_revision" integer NOT NULL, + "current_billing_price_entry_id" uuid NOT NULL REFERENCES "billing_price_entries"("id") ON DELETE RESTRICT, + "current_plan" text NOT NULL, + "current_interval" text NOT NULL, + "target_catalog_revision" integer NOT NULL, + "target_billing_price_entry_id" uuid NOT NULL REFERENCES "billing_price_entries"("id") ON DELETE RESTRICT, + "target_plan" text NOT NULL, + "target_interval" text NOT NULL, + "target_offer_key" text NOT NULL, + "effective_at" text NOT NULL, + "proration_mode" text NOT NULL, + "provider_payment_id" text, + "payment_url_encrypted" text, + "status" text NOT NULL DEFAULT 'creating', + "last_error" text, + "completed_at" timestamptz, + "created_at" timestamptz NOT NULL DEFAULT now(), + "updated_at" timestamptz NOT NULL DEFAULT now(), + CONSTRAINT "billing_plan_change_attempts_status_check" CHECK ("status" IN ('creating', 'pending', 'succeeded', 'failed', 'conflicted')), + CONSTRAINT "billing_plan_change_attempts_effective_at_check" CHECK ("effective_at" IN ('immediately', 'next_billing_date')), + CONSTRAINT "billing_plan_change_attempts_proration_mode_check" CHECK ("proration_mode" IN ('prorated_immediately', 'do_not_bill')), + CONSTRAINT "billing_plan_change_attempts_current_plan_check" CHECK ("current_plan" IN ('pro')), + CONSTRAINT "billing_plan_change_attempts_target_plan_check" CHECK ("target_plan" IN ('pro')) +); +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_plan_change_attempts_idempotency_uidx" ON "billing_plan_change_attempts" ("idempotency_key"); +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_plan_change_attempts_entity_nonterminal_uidx" ON "billing_plan_change_attempts" ("billable_entity_id") WHERE "status" IN ('creating', 'pending'); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "billing_plan_states" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), + "billable_entity_id" text NOT NULL UNIQUE REFERENCES "user"("id") ON DELETE RESTRICT, + "active_subscription_id" uuid REFERENCES "billing_subscriptions"("id") ON DELETE RESTRICT, + "projection_version" integer NOT NULL DEFAULT 0, + "created_at" timestamptz NOT NULL DEFAULT now(), + "updated_at" timestamptz NOT NULL DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "billing_webhook_events" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), + "provider" text NOT NULL, + "provider_event_id" text NOT NULL, + "event_type" text NOT NULL, + "occurred_at" timestamptz NOT NULL, + "subscription_id" text, + "checkout_attempt_id" text, + "payload_encrypted" text, + "payload_key_version" text, + "verified_key_version" text, + "status" text NOT NULL DEFAULT 'pending', + "processing_attempts" integer NOT NULL DEFAULT 0, + "last_error" text, + "available_at" timestamptz NOT NULL DEFAULT now(), + "locked_at" timestamptz, + "lease_expires_at" timestamptz, + "worker_id" text, + "received_at" timestamptz NOT NULL DEFAULT now(), + "processed_at" timestamptz, + CONSTRAINT "billing_webhook_events_status_check" CHECK ("status" IN ('pending', 'processing', 'processed', 'ignored', 'quarantined', 'failed')) +); +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_webhook_events_provider_event_uidx" ON "billing_webhook_events" ("provider", "provider_event_id"); +--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "billing_webhook_events_queue_idx" ON "billing_webhook_events" ("status", "available_at"); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "billing_reconciliation_jobs" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), + "provider" text NOT NULL, + "checkout_attempt_id" uuid REFERENCES "billing_checkout_attempts"("id") ON DELETE RESTRICT, + "plan_change_attempt_id" uuid REFERENCES "billing_plan_change_attempts"("id") ON DELETE RESTRICT, + "subscription_id" uuid REFERENCES "billing_subscriptions"("id") ON DELETE RESTRICT, + "provider_customer_id" uuid REFERENCES "billing_provider_customers"("id") ON DELETE RESTRICT, + "operation" text NOT NULL DEFAULT 'reconcile', + "status" text NOT NULL DEFAULT 'pending', + "attempt_count" integer NOT NULL DEFAULT 0, + "available_at" timestamptz NOT NULL DEFAULT now(), + "locked_at" timestamptz, + "lease_expires_at" timestamptz, + "worker_id" text, + "last_error" text, + "created_at" timestamptz NOT NULL DEFAULT now(), + "updated_at" timestamptz NOT NULL DEFAULT now(), + CONSTRAINT "billing_reconciliation_jobs_exactly_one_subject" CHECK ( + (CASE WHEN "checkout_attempt_id" IS NOT NULL THEN 1 ELSE 0 END) + + (CASE WHEN "plan_change_attempt_id" IS NOT NULL THEN 1 ELSE 0 END) + + (CASE WHEN "subscription_id" IS NOT NULL THEN 1 ELSE 0 END) + + (CASE WHEN "provider_customer_id" IS NOT NULL THEN 1 ELSE 0 END) = 1 + ), + CONSTRAINT "billing_reconciliation_jobs_status_check" CHECK ("status" IN ('pending', 'processing', 'failed', 'completed', 'quarantined')), + CONSTRAINT "billing_reconciliation_jobs_operation_check" CHECK ("operation" = 'reconcile' OR ("operation" = 'cancellation' AND "subscription_id" IS NOT NULL)) +); +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_reconciliation_jobs_live_checkout_uidx" ON "billing_reconciliation_jobs" ("checkout_attempt_id") WHERE "checkout_attempt_id" IS NOT NULL AND "status" IN ('pending', 'processing', 'failed'); +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_reconciliation_jobs_live_plan_change_uidx" ON "billing_reconciliation_jobs" ("plan_change_attempt_id") WHERE "plan_change_attempt_id" IS NOT NULL AND "status" IN ('pending', 'processing', 'failed'); +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_reconciliation_jobs_live_subscription_uidx" ON "billing_reconciliation_jobs" ("subscription_id") WHERE "subscription_id" IS NOT NULL AND "status" IN ('pending', 'processing', 'failed'); +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "billing_reconciliation_jobs_live_customer_uidx" ON "billing_reconciliation_jobs" ("provider_customer_id") WHERE "provider_customer_id" IS NOT NULL AND "status" IN ('pending', 'processing', 'failed'); diff --git a/apps/api/drizzle/0003_auth_account_issuer_nullable.sql b/apps/api/drizzle/0003_auth_account_issuer_nullable.sql new file mode 100644 index 00000000..8c9b3b97 --- /dev/null +++ b/apps/api/drizzle/0003_auth_account_issuer_nullable.sql @@ -0,0 +1,2 @@ +-- Better Auth 1.7.7 no longer writes account.issuer. Preserve existing values. +ALTER TABLE "account" ALTER COLUMN "issuer" DROP NOT NULL; diff --git a/apps/api/drizzle/0004_drop_legacy_revoked_tokens.sql b/apps/api/drizzle/0004_drop_legacy_revoked_tokens.sql new file mode 100644 index 00000000..8bb4c436 --- /dev/null +++ b/apps/api/drizzle/0004_drop_legacy_revoked_tokens.sql @@ -0,0 +1 @@ +DROP TABLE "legacy_revoked_tokens" CASCADE; diff --git a/apps/api/drizzle/0005_drop_account_issuer.sql b/apps/api/drizzle/0005_drop_account_issuer.sql new file mode 100644 index 00000000..3ce8eb52 --- /dev/null +++ b/apps/api/drizzle/0005_drop_account_issuer.sql @@ -0,0 +1,2 @@ +-- Better Auth 1.7.7 no longer reads or writes account.issuer (relaxed in 0003). +ALTER TABLE "account" DROP COLUMN IF EXISTS "issuer"; diff --git a/apps/api/drizzle/0006_oauth_app_selections.sql b/apps/api/drizzle/0006_oauth_app_selections.sql new file mode 100644 index 00000000..f81fa3bc --- /dev/null +++ b/apps/api/drizzle/0006_oauth_app_selections.sql @@ -0,0 +1,8 @@ +CREATE TABLE "oauth_app_selections" ( + "session_id" text PRIMARY KEY NOT NULL, + "api_key_id" text NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +ALTER TABLE "oauth_app_selections" ADD CONSTRAINT "oauth_app_selections_session_id_session_id_fk" FOREIGN KEY ("session_id") REFERENCES "public"."session"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "oauth_app_selections" ADD CONSTRAINT "oauth_app_selections_api_key_id_api_keys_id_fk" FOREIGN KEY ("api_key_id") REFERENCES "public"."api_keys"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/apps/api/drizzle/meta/0002_snapshot.json b/apps/api/drizzle/meta/0002_snapshot.json new file mode 100644 index 00000000..f311c0ee --- /dev/null +++ b/apps/api/drizzle/meta/0002_snapshot.json @@ -0,0 +1,3850 @@ +{ + "id": "1e1d6622-db44-4a0f-a07b-59bac874def4", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "issuer": { + "name": "issuer", + "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 + }, + "user_id": { + "name": "user_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 + }, + "id_token": { + "name": "id_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 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.jwks": { + "name": "jwks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "alg": { + "name": "alg", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "crv": { + "name": "crv", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_access_token": { + "name": "oauth_access_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorization_code_id": { + "name": "authorization_code_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "refresh_id": { + "name": "refresh_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked": { + "name": "revoked", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "confirmation": { + "name": "confirmation", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_access_token_client_id_oauth_client_client_id_fk": { + "name": "oauth_access_token_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_access_token_session_id_session_id_fk": { + "name": "oauth_access_token_session_id_session_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "session", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "oauth_access_token_user_id_user_id_fk": { + "name": "oauth_access_token_user_id_user_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_access_token_refresh_id_oauth_refresh_token_id_fk": { + "name": "oauth_access_token_refresh_id_oauth_refresh_token_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "oauth_refresh_token", + "columnsFrom": [ + "refresh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_access_token_token_unique": { + "name": "oauth_access_token_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_client": { + "name": "oauth_client", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_discovery_id": { + "name": "client_discovery_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "skip_consent": { + "name": "skip_consent", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "enable_end_session": { + "name": "enable_end_session", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subject_type": { + "name": "subject_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "client_credentials_scopes": { + "name": "client_credentials_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contacts": { + "name": "contacts", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "tos": { + "name": "tos", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "policy": { + "name": "policy", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_id": { + "name": "software_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_version": { + "name": "software_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_statement": { + "name": "software_statement", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uris": { + "name": "redirect_uris", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "post_logout_redirect_uris": { + "name": "post_logout_redirect_uris", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "backchannel_logout_uri": { + "name": "backchannel_logout_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "backchannel_logout_session_required": { + "name": "backchannel_logout_session_required", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "token_endpoint_auth_method": { + "name": "token_endpoint_auth_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "application_type": { + "name": "application_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "jwks": { + "name": "jwks", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "jwks_uri": { + "name": "jwks_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "grant_types": { + "name": "grant_types", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "response_types": { + "name": "response_types", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "require_pkce": { + "name": "require_pkce", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "dpop_bound_access_tokens": { + "name": "dpop_bound_access_tokens", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_client_user_id_user_id_fk": { + "name": "oauth_client_user_id_user_id_fk", + "tableFrom": "oauth_client", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_client_client_id_unique": { + "name": "oauth_client_client_id_unique", + "nullsNotDistinct": false, + "columns": [ + "client_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_client_assertion": { + "name": "oauth_client_assertion", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_client_resource": { + "name": "oauth_client_resource", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_client_resource_client_id_oauth_client_client_id_fk": { + "name": "oauth_client_resource_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_client_resource", + "tableTo": "oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_client_resource_resource_id_oauth_resource_identifier_fk": { + "name": "oauth_client_resource_resource_id_oauth_resource_identifier_fk", + "tableFrom": "oauth_client_resource", + "tableTo": "oauth_resource", + "columnsFrom": [ + "resource_id" + ], + "columnsTo": [ + "identifier" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_consent": { + "name": "oauth_consent", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_consent_client_id_oauth_client_client_id_fk": { + "name": "oauth_consent_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_consent", + "tableTo": "oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_consent_user_id_user_id_fk": { + "name": "oauth_consent_user_id_user_id_fk", + "tableFrom": "oauth_consent", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_refresh_token": { + "name": "oauth_refresh_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorization_code_id": { + "name": "authorization_code_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked": { + "name": "revoked", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "rotated_at": { + "name": "rotated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "rotation_replay_response": { + "name": "rotation_replay_response", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rotation_replay_expires_at": { + "name": "rotation_replay_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "auth_time": { + "name": "auth_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "confirmation": { + "name": "confirmation", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_refresh_token_client_id_oauth_client_client_id_fk": { + "name": "oauth_refresh_token_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_refresh_token", + "tableTo": "oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_refresh_token_session_id_session_id_fk": { + "name": "oauth_refresh_token_session_id_session_id_fk", + "tableFrom": "oauth_refresh_token", + "tableTo": "session", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "oauth_refresh_token_user_id_user_id_fk": { + "name": "oauth_refresh_token_user_id_user_id_fk", + "tableFrom": "oauth_refresh_token", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_refresh_token_token_unique": { + "name": "oauth_refresh_token_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_resource": { + "name": "oauth_resource", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token_ttl": { + "name": "access_token_ttl", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "refresh_token_ttl": { + "name": "refresh_token_ttl", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "signing_algorithm": { + "name": "signing_algorithm", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "signing_key_id": { + "name": "signing_key_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "allowed_scopes": { + "name": "allowed_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "custom_claims": { + "name": "custom_claims", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "dpop_bound_access_tokens_required": { + "name": "dpop_bound_access_tokens_required", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "policy_version": { + "name": "policy_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_resource_identifier_unique": { + "name": "oauth_resource_identifier_unique", + "nullsNotDistinct": false, + "columns": [ + "identifier" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_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 + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "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 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.api_keys": { + "name": "api_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "key_id": { + "name": "key_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "restriction": { + "name": "restriction", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_referrers": { + "name": "http_referrers", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "ip_addresses": { + "name": "ip_addresses", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "api_keys_user_name_uidx": { + "name": "api_keys_user_name_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"api_keys\".\"deleted\" = false", + "concurrently": false, + "method": "btree", + "with": {} + }, + "api_keys_one_default_uidx": { + "name": "api_keys_one_default_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"api_keys\".\"is_default\" = true AND \"api_keys\".\"deleted\" = false", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "api_keys_user_id_user_id_fk": { + "name": "api_keys_user_id_user_id_fk", + "tableFrom": "api_keys", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_keys_key_id_unique": { + "name": "api_keys_key_id_unique", + "nullsNotDistinct": false, + "columns": [ + "key_id" + ] + }, + "api_keys_key_unique": { + "name": "api_keys_key_unique", + "nullsNotDistinct": false, + "columns": [ + "key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.legacy_revoked_tokens": { + "name": "legacy_revoked_tokens", + "schema": "", + "columns": { + "jti": { + "name": "jti", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.media": { + "name": "media", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media_id": { + "name": "media_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apikey": { + "name": "apikey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "original_file_name": { + "name": "original_file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "thumbnail_generated": { + "name": "thumbnail_generated", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "access_control": { + "name": "access_control", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'private'" + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "caption": { + "name": "caption", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "temp": { + "name": "temp", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "media_user_id_user_id_fk": { + "name": "media_user_id_user_id_fk", + "tableFrom": "media", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "media_media_id_unique": { + "name": "media_media_id_unique", + "nullsNotDistinct": false, + "columns": [ + "media_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.media_settings": { + "name": "media_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apikey": { + "name": "apikey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "use_web_p": { + "name": "use_web_p", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "webp_output_quality": { + "name": "webp_output_quality", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "thumbnail_width": { + "name": "thumbnail_width", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "thumbnail_height": { + "name": "thumbnail_height", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "media_settings_user_id_user_id_fk": { + "name": "media_settings_user_id_user_id_fk", + "tableFrom": "media_settings", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "media_settings_apikey_unique": { + "name": "media_settings_apikey_unique", + "nullsNotDistinct": false, + "columns": [ + "apikey" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.profiles": { + "name": "profiles", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "public_user_id": { + "name": "public_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subscription_id": { + "name": "subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subscription_ends_after": { + "name": "subscription_ends_after", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "subscription_method": { + "name": "subscription_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subscription_status": { + "name": "subscription_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'not-subscribed'" + } + }, + "indexes": {}, + "foreignKeys": { + "profiles_user_id_user_id_fk": { + "name": "profiles_user_id_user_id_fk", + "tableFrom": "profiles", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "profiles_public_user_id_unique": { + "name": "profiles_public_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "public_user_id" + ] + }, + "profiles_customer_id_unique": { + "name": "profiles_customer_id_unique", + "nullsNotDistinct": false, + "columns": [ + "customer_id" + ] + }, + "profiles_subscription_id_unique": { + "name": "profiles_subscription_id_unique", + "nullsNotDistinct": false, + "columns": [ + "subscription_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.signatures": { + "name": "signatures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apikey": { + "name": "apikey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "signature": { + "name": "signature", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "valid_till": { + "name": "valid_till", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "signatures_signature_unique": { + "name": "signatures_signature_unique", + "nullsNotDistinct": false, + "columns": [ + "signature" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tus_uploads": { + "name": "tus_uploads", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "upload_id": { + "name": "upload_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apikey": { + "name": "apikey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "upload_length": { + "name": "upload_length", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "upload_offset": { + "name": "upload_offset", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "temp_file_path": { + "name": "temp_file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "signature": { + "name": "signature", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_complete": { + "name": "is_complete", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "tus_uploads_upload_id_unique": { + "name": "tus_uploads_upload_id_unique", + "nullsNotDistinct": false, + "columns": [ + "upload_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.billing_catalog_revision_items": { + "name": "billing_catalog_revision_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "catalog_revision_id": { + "name": "catalog_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "offer_key": { + "name": "offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_price_entry_id": { + "name": "billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "billing_catalog_revision_items_revision_key_uidx": { + "name": "billing_catalog_revision_items_revision_key_uidx", + "columns": [ + { + "expression": "catalog_revision_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "offer_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_catalog_revision_items_revision_price_uidx": { + "name": "billing_catalog_revision_items_revision_price_uidx", + "columns": [ + { + "expression": "catalog_revision_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "billing_price_entry_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_catalog_revision_items_catalog_revision_id_billing_catalog_revisions_id_fk": { + "name": "billing_catalog_revision_items_catalog_revision_id_billing_catalog_revisions_id_fk", + "tableFrom": "billing_catalog_revision_items", + "tableTo": "billing_catalog_revisions", + "columnsFrom": [ + "catalog_revision_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "billing_catalog_revision_items_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_catalog_revision_items_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_catalog_revision_items", + "tableTo": "billing_price_entries", + "columnsFrom": [ + "billing_price_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.billing_catalog_revisions": { + "name": "billing_catalog_revisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "checkout_provider": { + "name": "checkout_provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending_verification'" + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "activated_at": { + "name": "activated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "retired_at": { + "name": "retired_at", + "type": "timestamp with time zone", + "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": { + "billing_catalog_revisions_active_provider_uidx": { + "name": "billing_catalog_revisions_active_provider_uidx", + "columns": [ + { + "expression": "checkout_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_catalog_revisions\".\"status\" = 'active'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "billing_catalog_revisions_revision_unique": { + "name": "billing_catalog_revisions_revision_unique", + "nullsNotDistinct": false, + "columns": [ + "revision" + ] + } + }, + "policies": {}, + "checkConstraints": { + "billing_catalog_revisions_status_check": { + "name": "billing_catalog_revisions_status_check", + "value": "\"billing_catalog_revisions\".\"status\" IN ('pending_verification', 'active', 'retired', 'invalid', 'abandoned')" + }, + "billing_catalog_revisions_revision_check": { + "name": "billing_catalog_revisions_revision_check", + "value": "\"billing_catalog_revisions\".\"revision\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.billing_checkout_attempts": { + "name": "billing_checkout_attempts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "attempt_id": { + "name": "attempt_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billable_entity_id": { + "name": "billable_entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_id": { + "name": "payer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_email": { + "name": "payer_email", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "return_url": { + "name": "return_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "catalog_revision": { + "name": "catalog_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "offer_key": { + "name": "offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_plan": { + "name": "requested_plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_interval": { + "name": "requested_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_price_entry_id": { + "name": "billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "quoted_amount_minor": { + "name": "quoted_amount_minor", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "quoted_currency": { + "name": "quoted_currency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_customer_id": { + "name": "billing_customer_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider_checkout_session_id": { + "name": "provider_checkout_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "checkout_url_encrypted": { + "name": "checkout_url_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'creating'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "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": { + "billing_checkout_attempts_provider_session_uidx": { + "name": "billing_checkout_attempts_provider_session_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_checkout_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_checkout_attempts\".\"provider_checkout_session_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_checkout_attempts_idempotency_uidx": { + "name": "billing_checkout_attempts_idempotency_uidx", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_checkout_attempts_entity_nonterminal_uidx": { + "name": "billing_checkout_attempts_entity_nonterminal_uidx", + "columns": [ + { + "expression": "billable_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_checkout_attempts\".\"status\" IN ('creating', 'open')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_checkout_attempts_billable_entity_id_user_id_fk": { + "name": "billing_checkout_attempts_billable_entity_id_user_id_fk", + "tableFrom": "billing_checkout_attempts", + "tableTo": "user", + "columnsFrom": [ + "billable_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_checkout_attempts_payer_id_user_id_fk": { + "name": "billing_checkout_attempts_payer_id_user_id_fk", + "tableFrom": "billing_checkout_attempts", + "tableTo": "user", + "columnsFrom": [ + "payer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_checkout_attempts_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_checkout_attempts_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_checkout_attempts", + "tableTo": "billing_price_entries", + "columnsFrom": [ + "billing_price_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_checkout_attempts_billing_customer_id_billing_provider_customers_id_fk": { + "name": "billing_checkout_attempts_billing_customer_id_billing_provider_customers_id_fk", + "tableFrom": "billing_checkout_attempts", + "tableTo": "billing_provider_customers", + "columnsFrom": [ + "billing_customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "billing_checkout_attempts_attempt_id_unique": { + "name": "billing_checkout_attempts_attempt_id_unique", + "nullsNotDistinct": false, + "columns": [ + "attempt_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "billing_checkout_attempts_status_check": { + "name": "billing_checkout_attempts_status_check", + "value": "\"billing_checkout_attempts\".\"status\" IN ('creating', 'open', 'completed', 'expired', 'abandoned', 'conflicted')" + }, + "billing_checkout_attempts_amount_check": { + "name": "billing_checkout_attempts_amount_check", + "value": "\"billing_checkout_attempts\".\"quoted_amount_minor\" > 0" + }, + "billing_checkout_attempts_plan_check": { + "name": "billing_checkout_attempts_plan_check", + "value": "\"billing_checkout_attempts\".\"requested_plan\" IN ('pro')" + }, + "billing_checkout_attempts_interval_check": { + "name": "billing_checkout_attempts_interval_check", + "value": "\"billing_checkout_attempts\".\"requested_interval\" IN ('month', 'year')" + } + }, + "isRLSEnabled": false + }, + "public.billing_plan_change_attempts": { + "name": "billing_plan_change_attempts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "change_id": { + "name": "change_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billable_entity_id": { + "name": "billable_entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subscription_id": { + "name": "subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_id": { + "name": "payer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "current_catalog_revision": { + "name": "current_catalog_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "current_billing_price_entry_id": { + "name": "current_billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "current_plan": { + "name": "current_plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "current_interval": { + "name": "current_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_catalog_revision": { + "name": "target_catalog_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "target_billing_price_entry_id": { + "name": "target_billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "target_plan": { + "name": "target_plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_interval": { + "name": "target_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_offer_key": { + "name": "target_offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "effective_at": { + "name": "effective_at", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "proration_mode": { + "name": "proration_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_payment_id": { + "name": "provider_payment_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payment_url_encrypted": { + "name": "payment_url_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'creating'" + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "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": { + "billing_plan_change_attempts_idempotency_uidx": { + "name": "billing_plan_change_attempts_idempotency_uidx", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_plan_change_attempts_entity_nonterminal_uidx": { + "name": "billing_plan_change_attempts_entity_nonterminal_uidx", + "columns": [ + { + "expression": "billable_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_plan_change_attempts\".\"status\" IN ('creating', 'pending')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_plan_change_attempts_billable_entity_id_user_id_fk": { + "name": "billing_plan_change_attempts_billable_entity_id_user_id_fk", + "tableFrom": "billing_plan_change_attempts", + "tableTo": "user", + "columnsFrom": [ + "billable_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_plan_change_attempts_subscription_id_billing_subscriptions_id_fk": { + "name": "billing_plan_change_attempts_subscription_id_billing_subscriptions_id_fk", + "tableFrom": "billing_plan_change_attempts", + "tableTo": "billing_subscriptions", + "columnsFrom": [ + "subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_plan_change_attempts_payer_id_user_id_fk": { + "name": "billing_plan_change_attempts_payer_id_user_id_fk", + "tableFrom": "billing_plan_change_attempts", + "tableTo": "user", + "columnsFrom": [ + "payer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_plan_change_attempts_current_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_plan_change_attempts_current_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_plan_change_attempts", + "tableTo": "billing_price_entries", + "columnsFrom": [ + "current_billing_price_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_plan_change_attempts_target_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_plan_change_attempts_target_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_plan_change_attempts", + "tableTo": "billing_price_entries", + "columnsFrom": [ + "target_billing_price_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "billing_plan_change_attempts_change_id_unique": { + "name": "billing_plan_change_attempts_change_id_unique", + "nullsNotDistinct": false, + "columns": [ + "change_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "billing_plan_change_attempts_status_check": { + "name": "billing_plan_change_attempts_status_check", + "value": "\"billing_plan_change_attempts\".\"status\" IN ('creating', 'pending', 'succeeded', 'failed', 'conflicted')" + }, + "billing_plan_change_attempts_effective_at_check": { + "name": "billing_plan_change_attempts_effective_at_check", + "value": "\"billing_plan_change_attempts\".\"effective_at\" IN ('immediately', 'next_billing_date')" + }, + "billing_plan_change_attempts_proration_mode_check": { + "name": "billing_plan_change_attempts_proration_mode_check", + "value": "\"billing_plan_change_attempts\".\"proration_mode\" IN ('prorated_immediately', 'do_not_bill')" + }, + "billing_plan_change_attempts_current_plan_check": { + "name": "billing_plan_change_attempts_current_plan_check", + "value": "\"billing_plan_change_attempts\".\"current_plan\" IN ('pro')" + }, + "billing_plan_change_attempts_target_plan_check": { + "name": "billing_plan_change_attempts_target_plan_check", + "value": "\"billing_plan_change_attempts\".\"target_plan\" IN ('pro')" + } + }, + "isRLSEnabled": false + }, + "public.billing_plan_states": { + "name": "billing_plan_states", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "billable_entity_id": { + "name": "billable_entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active_subscription_id": { + "name": "active_subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "projection_version": { + "name": "projection_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "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": { + "billing_plan_states_billable_entity_id_user_id_fk": { + "name": "billing_plan_states_billable_entity_id_user_id_fk", + "tableFrom": "billing_plan_states", + "tableTo": "user", + "columnsFrom": [ + "billable_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_plan_states_active_subscription_id_billing_subscriptions_id_fk": { + "name": "billing_plan_states_active_subscription_id_billing_subscriptions_id_fk", + "tableFrom": "billing_plan_states", + "tableTo": "billing_subscriptions", + "columnsFrom": [ + "active_subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "billing_plan_states_billable_entity_id_unique": { + "name": "billing_plan_states_billable_entity_id_unique", + "nullsNotDistinct": false, + "columns": [ + "billable_entity_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.billing_price_entries": { + "name": "billing_price_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "offer_key": { + "name": "offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_interval": { + "name": "billing_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount_minor": { + "name": "amount_minor", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "provider_trial_days": { + "name": "provider_trial_days", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_product_id": { + "name": "provider_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp with time zone", + "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": { + "billing_price_entries_provider_product_uidx": { + "name": "billing_price_entries_provider_product_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_price_entries_offer_key_idx": { + "name": "billing_price_entries_offer_key_idx", + "columns": [ + { + "expression": "offer_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_price_entries_amount_check": { + "name": "billing_price_entries_amount_check", + "value": "\"billing_price_entries\".\"amount_minor\" > 0" + }, + "billing_price_entries_trial_days_check": { + "name": "billing_price_entries_trial_days_check", + "value": "\"billing_price_entries\".\"provider_trial_days\" >= 0" + }, + "billing_price_entries_currency_check": { + "name": "billing_price_entries_currency_check", + "value": "\"billing_price_entries\".\"currency\" ~ '^[A-Z]{3}$'" + }, + "billing_price_entries_plan_check": { + "name": "billing_price_entries_plan_check", + "value": "\"billing_price_entries\".\"plan\" IN ('pro')" + }, + "billing_price_entries_interval_check": { + "name": "billing_price_entries_interval_check", + "value": "\"billing_price_entries\".\"billing_interval\" IN ('month', 'year')" + } + }, + "isRLSEnabled": false + }, + "public.billing_provider_customers": { + "name": "billing_provider_customers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_id": { + "name": "payer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_email": { + "name": "payer_email", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "provider_customer_id": { + "name": "provider_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'creating'" + }, + "last_error": { + "name": "last_error", + "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": { + "billing_provider_customers_provider_payer_uidx": { + "name": "billing_provider_customers_provider_payer_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "payer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_provider_customers_provider_customer_uidx": { + "name": "billing_provider_customers_provider_customer_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_customer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_provider_customers\".\"provider_customer_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_provider_customers_idempotency_uidx": { + "name": "billing_provider_customers_idempotency_uidx", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_provider_customers_payer_id_user_id_fk": { + "name": "billing_provider_customers_payer_id_user_id_fk", + "tableFrom": "billing_provider_customers", + "tableTo": "user", + "columnsFrom": [ + "payer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_provider_customers_status_check": { + "name": "billing_provider_customers_status_check", + "value": "\"billing_provider_customers\".\"status\" IN ('creating', 'active', 'conflicted')" + } + }, + "isRLSEnabled": false + }, + "public.billing_reconciliation_jobs": { + "name": "billing_reconciliation_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "checkout_attempt_id": { + "name": "checkout_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "plan_change_attempt_id": { + "name": "plan_change_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "subscription_id": { + "name": "subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider_customer_id": { + "name": "provider_customer_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "operation": { + "name": "operation", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'reconcile'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "available_at": { + "name": "available_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "locked_at": { + "name": "locked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "worker_id": { + "name": "worker_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "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": { + "billing_reconciliation_jobs_live_checkout_uidx": { + "name": "billing_reconciliation_jobs_live_checkout_uidx", + "columns": [ + { + "expression": "checkout_attempt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_reconciliation_jobs\".\"checkout_attempt_id\" IS NOT NULL AND \"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_reconciliation_jobs_live_plan_change_uidx": { + "name": "billing_reconciliation_jobs_live_plan_change_uidx", + "columns": [ + { + "expression": "plan_change_attempt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_reconciliation_jobs\".\"plan_change_attempt_id\" IS NOT NULL AND \"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_reconciliation_jobs_live_subscription_uidx": { + "name": "billing_reconciliation_jobs_live_subscription_uidx", + "columns": [ + { + "expression": "subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_reconciliation_jobs\".\"subscription_id\" IS NOT NULL AND \"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_reconciliation_jobs_live_customer_uidx": { + "name": "billing_reconciliation_jobs_live_customer_uidx", + "columns": [ + { + "expression": "provider_customer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_reconciliation_jobs\".\"provider_customer_id\" IS NOT NULL AND \"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_reconciliation_jobs_checkout_attempt_id_billing_checkout_attempts_id_fk": { + "name": "billing_reconciliation_jobs_checkout_attempt_id_billing_checkout_attempts_id_fk", + "tableFrom": "billing_reconciliation_jobs", + "tableTo": "billing_checkout_attempts", + "columnsFrom": [ + "checkout_attempt_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_reconciliation_jobs_plan_change_attempt_id_billing_plan_change_attempts_id_fk": { + "name": "billing_reconciliation_jobs_plan_change_attempt_id_billing_plan_change_attempts_id_fk", + "tableFrom": "billing_reconciliation_jobs", + "tableTo": "billing_plan_change_attempts", + "columnsFrom": [ + "plan_change_attempt_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_reconciliation_jobs_subscription_id_billing_subscriptions_id_fk": { + "name": "billing_reconciliation_jobs_subscription_id_billing_subscriptions_id_fk", + "tableFrom": "billing_reconciliation_jobs", + "tableTo": "billing_subscriptions", + "columnsFrom": [ + "subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_reconciliation_jobs_provider_customer_id_billing_provider_customers_id_fk": { + "name": "billing_reconciliation_jobs_provider_customer_id_billing_provider_customers_id_fk", + "tableFrom": "billing_reconciliation_jobs", + "tableTo": "billing_provider_customers", + "columnsFrom": [ + "provider_customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_reconciliation_jobs_exactly_one_subject": { + "name": "billing_reconciliation_jobs_exactly_one_subject", + "value": "((CASE WHEN \"billing_reconciliation_jobs\".\"checkout_attempt_id\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"billing_reconciliation_jobs\".\"plan_change_attempt_id\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"billing_reconciliation_jobs\".\"subscription_id\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"billing_reconciliation_jobs\".\"provider_customer_id\" IS NOT NULL THEN 1 ELSE 0 END)) = 1" + }, + "billing_reconciliation_jobs_status_check": { + "name": "billing_reconciliation_jobs_status_check", + "value": "\"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed', 'completed', 'quarantined')" + }, + "billing_reconciliation_jobs_operation_check": { + "name": "billing_reconciliation_jobs_operation_check", + "value": "\"billing_reconciliation_jobs\".\"operation\" = 'reconcile' OR (\"billing_reconciliation_jobs\".\"operation\" = 'cancellation' AND \"billing_reconciliation_jobs\".\"subscription_id\" IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.billing_subscriptions": { + "name": "billing_subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "billable_entity_id": { + "name": "billable_entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_customer_id": { + "name": "billing_customer_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "payer_id": { + "name": "payer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "origin_checkout_attempt_id": { + "name": "origin_checkout_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_subscription_id": { + "name": "provider_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_product_id": { + "name": "provider_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_price_entry_id": { + "name": "billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "catalog_revision": { + "name": "catalog_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "offer_key": { + "name": "offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_interval": { + "name": "billing_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "current_period_starts_at": { + "name": "current_period_starts_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "current_period_ends_at": { + "name": "current_period_ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "paid_through_at": { + "name": "paid_through_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "trial_ends_at": { + "name": "trial_ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cancel_at_period_end": { + "name": "cancel_at_period_end", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_entitlement_source": { + "name": "is_entitlement_source", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "provider_occurred_at": { + "name": "provider_occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "provider_version": { + "name": "provider_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_observed_at": { + "name": "last_observed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_reconciled_at": { + "name": "last_reconciled_at", + "type": "timestamp with time zone", + "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": { + "billing_subscriptions_provider_subscription_uidx": { + "name": "billing_subscriptions_provider_subscription_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_subscriptions_entity_source_uidx": { + "name": "billing_subscriptions_entity_source_uidx", + "columns": [ + { + "expression": "billable_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_subscriptions\".\"is_entitlement_source\" = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_subscriptions_billable_entity_id_user_id_fk": { + "name": "billing_subscriptions_billable_entity_id_user_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "user", + "columnsFrom": [ + "billable_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_subscriptions_billing_customer_id_billing_provider_customers_id_fk": { + "name": "billing_subscriptions_billing_customer_id_billing_provider_customers_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "billing_provider_customers", + "columnsFrom": [ + "billing_customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_subscriptions_payer_id_user_id_fk": { + "name": "billing_subscriptions_payer_id_user_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "user", + "columnsFrom": [ + "payer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_subscriptions_origin_checkout_attempt_id_billing_checkout_attempts_id_fk": { + "name": "billing_subscriptions_origin_checkout_attempt_id_billing_checkout_attempts_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "billing_checkout_attempts", + "columnsFrom": [ + "origin_checkout_attempt_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_subscriptions_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_subscriptions_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "billing_price_entries", + "columnsFrom": [ + "billing_price_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_subscriptions_status_check": { + "name": "billing_subscriptions_status_check", + "value": "\"billing_subscriptions\".\"status\" IN ('pending', 'trialing', 'active', 'past_due', 'cancelled', 'expired')" + }, + "billing_subscriptions_plan_check": { + "name": "billing_subscriptions_plan_check", + "value": "\"billing_subscriptions\".\"plan\" IN ('pro')" + }, + "billing_subscriptions_interval_check": { + "name": "billing_subscriptions_interval_check", + "value": "\"billing_subscriptions\".\"billing_interval\" IN ('month', 'year')" + } + }, + "isRLSEnabled": false + }, + "public.billing_webhook_events": { + "name": "billing_webhook_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_event_id": { + "name": "provider_event_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "subscription_id": { + "name": "subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "checkout_attempt_id": { + "name": "checkout_attempt_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload_encrypted": { + "name": "payload_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload_key_version": { + "name": "payload_key_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "verified_key_version": { + "name": "verified_key_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "processing_attempts": { + "name": "processing_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "available_at": { + "name": "available_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "locked_at": { + "name": "locked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "worker_id": { + "name": "worker_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "received_at": { + "name": "received_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "billing_webhook_events_provider_event_uidx": { + "name": "billing_webhook_events_provider_event_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_webhook_events_queue_idx": { + "name": "billing_webhook_events_queue_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "available_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_webhook_events_status_check": { + "name": "billing_webhook_events_status_check", + "value": "\"billing_webhook_events\".\"status\" IN ('pending', 'processing', 'processed', 'ignored', 'quarantined', 'failed')" + } + }, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/api/drizzle/meta/0004_snapshot.json b/apps/api/drizzle/meta/0004_snapshot.json new file mode 100644 index 00000000..ea450cb9 --- /dev/null +++ b/apps/api/drizzle/meta/0004_snapshot.json @@ -0,0 +1,3801 @@ +{ + "id": "a85ef515-4345-4e69-b2fd-39fa78d427ac", + "prevId": "1e1d6622-db44-4a0f-a07b-59bac874def4", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_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 + }, + "id_token": { + "name": "id_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 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.jwks": { + "name": "jwks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "alg": { + "name": "alg", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "crv": { + "name": "crv", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_access_token": { + "name": "oauth_access_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorization_code_id": { + "name": "authorization_code_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "refresh_id": { + "name": "refresh_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked": { + "name": "revoked", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "confirmation": { + "name": "confirmation", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_access_token_client_id_oauth_client_client_id_fk": { + "name": "oauth_access_token_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_access_token_session_id_session_id_fk": { + "name": "oauth_access_token_session_id_session_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "session", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "oauth_access_token_user_id_user_id_fk": { + "name": "oauth_access_token_user_id_user_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_access_token_refresh_id_oauth_refresh_token_id_fk": { + "name": "oauth_access_token_refresh_id_oauth_refresh_token_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "oauth_refresh_token", + "columnsFrom": [ + "refresh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_access_token_token_unique": { + "name": "oauth_access_token_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_client": { + "name": "oauth_client", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_discovery_id": { + "name": "client_discovery_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "skip_consent": { + "name": "skip_consent", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "enable_end_session": { + "name": "enable_end_session", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subject_type": { + "name": "subject_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "client_credentials_scopes": { + "name": "client_credentials_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contacts": { + "name": "contacts", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "tos": { + "name": "tos", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "policy": { + "name": "policy", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_id": { + "name": "software_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_version": { + "name": "software_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_statement": { + "name": "software_statement", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uris": { + "name": "redirect_uris", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "post_logout_redirect_uris": { + "name": "post_logout_redirect_uris", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "backchannel_logout_uri": { + "name": "backchannel_logout_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "backchannel_logout_session_required": { + "name": "backchannel_logout_session_required", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "token_endpoint_auth_method": { + "name": "token_endpoint_auth_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "application_type": { + "name": "application_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "jwks": { + "name": "jwks", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "jwks_uri": { + "name": "jwks_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "grant_types": { + "name": "grant_types", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "response_types": { + "name": "response_types", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "require_pkce": { + "name": "require_pkce", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "dpop_bound_access_tokens": { + "name": "dpop_bound_access_tokens", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_client_user_id_user_id_fk": { + "name": "oauth_client_user_id_user_id_fk", + "tableFrom": "oauth_client", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_client_client_id_unique": { + "name": "oauth_client_client_id_unique", + "nullsNotDistinct": false, + "columns": [ + "client_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_client_assertion": { + "name": "oauth_client_assertion", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_client_resource": { + "name": "oauth_client_resource", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_client_resource_client_id_oauth_client_client_id_fk": { + "name": "oauth_client_resource_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_client_resource", + "tableTo": "oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_client_resource_resource_id_oauth_resource_identifier_fk": { + "name": "oauth_client_resource_resource_id_oauth_resource_identifier_fk", + "tableFrom": "oauth_client_resource", + "tableTo": "oauth_resource", + "columnsFrom": [ + "resource_id" + ], + "columnsTo": [ + "identifier" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_consent": { + "name": "oauth_consent", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_consent_client_id_oauth_client_client_id_fk": { + "name": "oauth_consent_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_consent", + "tableTo": "oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_consent_user_id_user_id_fk": { + "name": "oauth_consent_user_id_user_id_fk", + "tableFrom": "oauth_consent", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_refresh_token": { + "name": "oauth_refresh_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorization_code_id": { + "name": "authorization_code_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked": { + "name": "revoked", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "rotated_at": { + "name": "rotated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "rotation_replay_response": { + "name": "rotation_replay_response", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rotation_replay_expires_at": { + "name": "rotation_replay_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "auth_time": { + "name": "auth_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "confirmation": { + "name": "confirmation", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_refresh_token_client_id_oauth_client_client_id_fk": { + "name": "oauth_refresh_token_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_refresh_token", + "tableTo": "oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_refresh_token_session_id_session_id_fk": { + "name": "oauth_refresh_token_session_id_session_id_fk", + "tableFrom": "oauth_refresh_token", + "tableTo": "session", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "oauth_refresh_token_user_id_user_id_fk": { + "name": "oauth_refresh_token_user_id_user_id_fk", + "tableFrom": "oauth_refresh_token", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_refresh_token_token_unique": { + "name": "oauth_refresh_token_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_resource": { + "name": "oauth_resource", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token_ttl": { + "name": "access_token_ttl", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "refresh_token_ttl": { + "name": "refresh_token_ttl", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "signing_algorithm": { + "name": "signing_algorithm", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "signing_key_id": { + "name": "signing_key_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "allowed_scopes": { + "name": "allowed_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "custom_claims": { + "name": "custom_claims", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "dpop_bound_access_tokens_required": { + "name": "dpop_bound_access_tokens_required", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "policy_version": { + "name": "policy_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_resource_identifier_unique": { + "name": "oauth_resource_identifier_unique", + "nullsNotDistinct": false, + "columns": [ + "identifier" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_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 + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "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 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.api_keys": { + "name": "api_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "key_id": { + "name": "key_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "restriction": { + "name": "restriction", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_referrers": { + "name": "http_referrers", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "ip_addresses": { + "name": "ip_addresses", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "api_keys_user_name_uidx": { + "name": "api_keys_user_name_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"api_keys\".\"deleted\" = false", + "concurrently": false, + "method": "btree", + "with": {} + }, + "api_keys_one_default_uidx": { + "name": "api_keys_one_default_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"api_keys\".\"is_default\" = true AND \"api_keys\".\"deleted\" = false", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "api_keys_user_id_user_id_fk": { + "name": "api_keys_user_id_user_id_fk", + "tableFrom": "api_keys", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_keys_key_id_unique": { + "name": "api_keys_key_id_unique", + "nullsNotDistinct": false, + "columns": [ + "key_id" + ] + }, + "api_keys_key_unique": { + "name": "api_keys_key_unique", + "nullsNotDistinct": false, + "columns": [ + "key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.media": { + "name": "media", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media_id": { + "name": "media_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apikey": { + "name": "apikey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "original_file_name": { + "name": "original_file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "thumbnail_generated": { + "name": "thumbnail_generated", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "access_control": { + "name": "access_control", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'private'" + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "caption": { + "name": "caption", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "temp": { + "name": "temp", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "media_user_id_user_id_fk": { + "name": "media_user_id_user_id_fk", + "tableFrom": "media", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "media_media_id_unique": { + "name": "media_media_id_unique", + "nullsNotDistinct": false, + "columns": [ + "media_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.media_settings": { + "name": "media_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apikey": { + "name": "apikey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "use_web_p": { + "name": "use_web_p", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "webp_output_quality": { + "name": "webp_output_quality", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "thumbnail_width": { + "name": "thumbnail_width", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "thumbnail_height": { + "name": "thumbnail_height", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "media_settings_user_id_user_id_fk": { + "name": "media_settings_user_id_user_id_fk", + "tableFrom": "media_settings", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "media_settings_apikey_unique": { + "name": "media_settings_apikey_unique", + "nullsNotDistinct": false, + "columns": [ + "apikey" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.profiles": { + "name": "profiles", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "public_user_id": { + "name": "public_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subscription_id": { + "name": "subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subscription_ends_after": { + "name": "subscription_ends_after", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "subscription_method": { + "name": "subscription_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subscription_status": { + "name": "subscription_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'not-subscribed'" + } + }, + "indexes": {}, + "foreignKeys": { + "profiles_user_id_user_id_fk": { + "name": "profiles_user_id_user_id_fk", + "tableFrom": "profiles", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "profiles_public_user_id_unique": { + "name": "profiles_public_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "public_user_id" + ] + }, + "profiles_customer_id_unique": { + "name": "profiles_customer_id_unique", + "nullsNotDistinct": false, + "columns": [ + "customer_id" + ] + }, + "profiles_subscription_id_unique": { + "name": "profiles_subscription_id_unique", + "nullsNotDistinct": false, + "columns": [ + "subscription_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.signatures": { + "name": "signatures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apikey": { + "name": "apikey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "signature": { + "name": "signature", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "valid_till": { + "name": "valid_till", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "signatures_signature_unique": { + "name": "signatures_signature_unique", + "nullsNotDistinct": false, + "columns": [ + "signature" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tus_uploads": { + "name": "tus_uploads", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "upload_id": { + "name": "upload_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apikey": { + "name": "apikey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "upload_length": { + "name": "upload_length", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "upload_offset": { + "name": "upload_offset", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "temp_file_path": { + "name": "temp_file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "signature": { + "name": "signature", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_complete": { + "name": "is_complete", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "tus_uploads_upload_id_unique": { + "name": "tus_uploads_upload_id_unique", + "nullsNotDistinct": false, + "columns": [ + "upload_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.billing_catalog_revision_items": { + "name": "billing_catalog_revision_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "catalog_revision_id": { + "name": "catalog_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "offer_key": { + "name": "offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_price_entry_id": { + "name": "billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "billing_catalog_revision_items_revision_key_uidx": { + "name": "billing_catalog_revision_items_revision_key_uidx", + "columns": [ + { + "expression": "catalog_revision_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "offer_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_catalog_revision_items_revision_price_uidx": { + "name": "billing_catalog_revision_items_revision_price_uidx", + "columns": [ + { + "expression": "catalog_revision_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "billing_price_entry_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_catalog_revision_items_catalog_revision_id_billing_catalog_revisions_id_fk": { + "name": "billing_catalog_revision_items_catalog_revision_id_billing_catalog_revisions_id_fk", + "tableFrom": "billing_catalog_revision_items", + "tableTo": "billing_catalog_revisions", + "columnsFrom": [ + "catalog_revision_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "billing_catalog_revision_items_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_catalog_revision_items_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_catalog_revision_items", + "tableTo": "billing_price_entries", + "columnsFrom": [ + "billing_price_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.billing_catalog_revisions": { + "name": "billing_catalog_revisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "checkout_provider": { + "name": "checkout_provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending_verification'" + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "activated_at": { + "name": "activated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "retired_at": { + "name": "retired_at", + "type": "timestamp with time zone", + "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": { + "billing_catalog_revisions_active_provider_uidx": { + "name": "billing_catalog_revisions_active_provider_uidx", + "columns": [ + { + "expression": "checkout_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_catalog_revisions\".\"status\" = 'active'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "billing_catalog_revisions_revision_unique": { + "name": "billing_catalog_revisions_revision_unique", + "nullsNotDistinct": false, + "columns": [ + "revision" + ] + } + }, + "policies": {}, + "checkConstraints": { + "billing_catalog_revisions_status_check": { + "name": "billing_catalog_revisions_status_check", + "value": "\"billing_catalog_revisions\".\"status\" IN ('pending_verification', 'active', 'retired', 'invalid', 'abandoned')" + }, + "billing_catalog_revisions_revision_check": { + "name": "billing_catalog_revisions_revision_check", + "value": "\"billing_catalog_revisions\".\"revision\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.billing_checkout_attempts": { + "name": "billing_checkout_attempts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "attempt_id": { + "name": "attempt_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billable_entity_id": { + "name": "billable_entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_id": { + "name": "payer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_email": { + "name": "payer_email", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "return_url": { + "name": "return_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "catalog_revision": { + "name": "catalog_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "offer_key": { + "name": "offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_plan": { + "name": "requested_plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_interval": { + "name": "requested_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_price_entry_id": { + "name": "billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "quoted_amount_minor": { + "name": "quoted_amount_minor", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "quoted_currency": { + "name": "quoted_currency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_customer_id": { + "name": "billing_customer_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider_checkout_session_id": { + "name": "provider_checkout_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "checkout_url_encrypted": { + "name": "checkout_url_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'creating'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "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": { + "billing_checkout_attempts_provider_session_uidx": { + "name": "billing_checkout_attempts_provider_session_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_checkout_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_checkout_attempts\".\"provider_checkout_session_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_checkout_attempts_idempotency_uidx": { + "name": "billing_checkout_attempts_idempotency_uidx", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_checkout_attempts_entity_nonterminal_uidx": { + "name": "billing_checkout_attempts_entity_nonterminal_uidx", + "columns": [ + { + "expression": "billable_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_checkout_attempts\".\"status\" IN ('creating', 'open')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_checkout_attempts_billable_entity_id_user_id_fk": { + "name": "billing_checkout_attempts_billable_entity_id_user_id_fk", + "tableFrom": "billing_checkout_attempts", + "tableTo": "user", + "columnsFrom": [ + "billable_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_checkout_attempts_payer_id_user_id_fk": { + "name": "billing_checkout_attempts_payer_id_user_id_fk", + "tableFrom": "billing_checkout_attempts", + "tableTo": "user", + "columnsFrom": [ + "payer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_checkout_attempts_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_checkout_attempts_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_checkout_attempts", + "tableTo": "billing_price_entries", + "columnsFrom": [ + "billing_price_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_checkout_attempts_billing_customer_id_billing_provider_customers_id_fk": { + "name": "billing_checkout_attempts_billing_customer_id_billing_provider_customers_id_fk", + "tableFrom": "billing_checkout_attempts", + "tableTo": "billing_provider_customers", + "columnsFrom": [ + "billing_customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "billing_checkout_attempts_attempt_id_unique": { + "name": "billing_checkout_attempts_attempt_id_unique", + "nullsNotDistinct": false, + "columns": [ + "attempt_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "billing_checkout_attempts_status_check": { + "name": "billing_checkout_attempts_status_check", + "value": "\"billing_checkout_attempts\".\"status\" IN ('creating', 'open', 'completed', 'expired', 'abandoned', 'conflicted')" + }, + "billing_checkout_attempts_amount_check": { + "name": "billing_checkout_attempts_amount_check", + "value": "\"billing_checkout_attempts\".\"quoted_amount_minor\" > 0" + }, + "billing_checkout_attempts_plan_check": { + "name": "billing_checkout_attempts_plan_check", + "value": "\"billing_checkout_attempts\".\"requested_plan\" IN ('pro')" + }, + "billing_checkout_attempts_interval_check": { + "name": "billing_checkout_attempts_interval_check", + "value": "\"billing_checkout_attempts\".\"requested_interval\" IN ('month', 'year')" + } + }, + "isRLSEnabled": false + }, + "public.billing_plan_change_attempts": { + "name": "billing_plan_change_attempts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "change_id": { + "name": "change_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billable_entity_id": { + "name": "billable_entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subscription_id": { + "name": "subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_id": { + "name": "payer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "current_catalog_revision": { + "name": "current_catalog_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "current_billing_price_entry_id": { + "name": "current_billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "current_plan": { + "name": "current_plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "current_interval": { + "name": "current_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_catalog_revision": { + "name": "target_catalog_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "target_billing_price_entry_id": { + "name": "target_billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "target_plan": { + "name": "target_plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_interval": { + "name": "target_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_offer_key": { + "name": "target_offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "effective_at": { + "name": "effective_at", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "proration_mode": { + "name": "proration_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_payment_id": { + "name": "provider_payment_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payment_url_encrypted": { + "name": "payment_url_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'creating'" + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "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": { + "billing_plan_change_attempts_idempotency_uidx": { + "name": "billing_plan_change_attempts_idempotency_uidx", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_plan_change_attempts_entity_nonterminal_uidx": { + "name": "billing_plan_change_attempts_entity_nonterminal_uidx", + "columns": [ + { + "expression": "billable_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_plan_change_attempts\".\"status\" IN ('creating', 'pending')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_plan_change_attempts_billable_entity_id_user_id_fk": { + "name": "billing_plan_change_attempts_billable_entity_id_user_id_fk", + "tableFrom": "billing_plan_change_attempts", + "tableTo": "user", + "columnsFrom": [ + "billable_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_plan_change_attempts_subscription_id_billing_subscriptions_id_fk": { + "name": "billing_plan_change_attempts_subscription_id_billing_subscriptions_id_fk", + "tableFrom": "billing_plan_change_attempts", + "tableTo": "billing_subscriptions", + "columnsFrom": [ + "subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_plan_change_attempts_payer_id_user_id_fk": { + "name": "billing_plan_change_attempts_payer_id_user_id_fk", + "tableFrom": "billing_plan_change_attempts", + "tableTo": "user", + "columnsFrom": [ + "payer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_plan_change_attempts_current_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_plan_change_attempts_current_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_plan_change_attempts", + "tableTo": "billing_price_entries", + "columnsFrom": [ + "current_billing_price_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_plan_change_attempts_target_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_plan_change_attempts_target_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_plan_change_attempts", + "tableTo": "billing_price_entries", + "columnsFrom": [ + "target_billing_price_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "billing_plan_change_attempts_change_id_unique": { + "name": "billing_plan_change_attempts_change_id_unique", + "nullsNotDistinct": false, + "columns": [ + "change_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "billing_plan_change_attempts_status_check": { + "name": "billing_plan_change_attempts_status_check", + "value": "\"billing_plan_change_attempts\".\"status\" IN ('creating', 'pending', 'succeeded', 'failed', 'conflicted')" + }, + "billing_plan_change_attempts_effective_at_check": { + "name": "billing_plan_change_attempts_effective_at_check", + "value": "\"billing_plan_change_attempts\".\"effective_at\" IN ('immediately', 'next_billing_date')" + }, + "billing_plan_change_attempts_proration_mode_check": { + "name": "billing_plan_change_attempts_proration_mode_check", + "value": "\"billing_plan_change_attempts\".\"proration_mode\" IN ('prorated_immediately', 'do_not_bill')" + }, + "billing_plan_change_attempts_current_plan_check": { + "name": "billing_plan_change_attempts_current_plan_check", + "value": "\"billing_plan_change_attempts\".\"current_plan\" IN ('pro')" + }, + "billing_plan_change_attempts_target_plan_check": { + "name": "billing_plan_change_attempts_target_plan_check", + "value": "\"billing_plan_change_attempts\".\"target_plan\" IN ('pro')" + } + }, + "isRLSEnabled": false + }, + "public.billing_plan_states": { + "name": "billing_plan_states", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "billable_entity_id": { + "name": "billable_entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active_subscription_id": { + "name": "active_subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "projection_version": { + "name": "projection_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "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": { + "billing_plan_states_billable_entity_id_user_id_fk": { + "name": "billing_plan_states_billable_entity_id_user_id_fk", + "tableFrom": "billing_plan_states", + "tableTo": "user", + "columnsFrom": [ + "billable_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_plan_states_active_subscription_id_billing_subscriptions_id_fk": { + "name": "billing_plan_states_active_subscription_id_billing_subscriptions_id_fk", + "tableFrom": "billing_plan_states", + "tableTo": "billing_subscriptions", + "columnsFrom": [ + "active_subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "billing_plan_states_billable_entity_id_unique": { + "name": "billing_plan_states_billable_entity_id_unique", + "nullsNotDistinct": false, + "columns": [ + "billable_entity_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.billing_price_entries": { + "name": "billing_price_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "offer_key": { + "name": "offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_interval": { + "name": "billing_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount_minor": { + "name": "amount_minor", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "provider_trial_days": { + "name": "provider_trial_days", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_product_id": { + "name": "provider_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp with time zone", + "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": { + "billing_price_entries_provider_product_uidx": { + "name": "billing_price_entries_provider_product_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_price_entries_offer_key_idx": { + "name": "billing_price_entries_offer_key_idx", + "columns": [ + { + "expression": "offer_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_price_entries_amount_check": { + "name": "billing_price_entries_amount_check", + "value": "\"billing_price_entries\".\"amount_minor\" > 0" + }, + "billing_price_entries_trial_days_check": { + "name": "billing_price_entries_trial_days_check", + "value": "\"billing_price_entries\".\"provider_trial_days\" >= 0" + }, + "billing_price_entries_currency_check": { + "name": "billing_price_entries_currency_check", + "value": "\"billing_price_entries\".\"currency\" ~ '^[A-Z]{3}$'" + }, + "billing_price_entries_plan_check": { + "name": "billing_price_entries_plan_check", + "value": "\"billing_price_entries\".\"plan\" IN ('pro')" + }, + "billing_price_entries_interval_check": { + "name": "billing_price_entries_interval_check", + "value": "\"billing_price_entries\".\"billing_interval\" IN ('month', 'year')" + } + }, + "isRLSEnabled": false + }, + "public.billing_provider_customers": { + "name": "billing_provider_customers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_id": { + "name": "payer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_email": { + "name": "payer_email", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "provider_customer_id": { + "name": "provider_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'creating'" + }, + "last_error": { + "name": "last_error", + "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": { + "billing_provider_customers_provider_payer_uidx": { + "name": "billing_provider_customers_provider_payer_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "payer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_provider_customers_provider_customer_uidx": { + "name": "billing_provider_customers_provider_customer_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_customer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_provider_customers\".\"provider_customer_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_provider_customers_idempotency_uidx": { + "name": "billing_provider_customers_idempotency_uidx", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_provider_customers_payer_id_user_id_fk": { + "name": "billing_provider_customers_payer_id_user_id_fk", + "tableFrom": "billing_provider_customers", + "tableTo": "user", + "columnsFrom": [ + "payer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_provider_customers_status_check": { + "name": "billing_provider_customers_status_check", + "value": "\"billing_provider_customers\".\"status\" IN ('creating', 'active', 'conflicted')" + } + }, + "isRLSEnabled": false + }, + "public.billing_reconciliation_jobs": { + "name": "billing_reconciliation_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "checkout_attempt_id": { + "name": "checkout_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "plan_change_attempt_id": { + "name": "plan_change_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "subscription_id": { + "name": "subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider_customer_id": { + "name": "provider_customer_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "operation": { + "name": "operation", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'reconcile'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "available_at": { + "name": "available_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "locked_at": { + "name": "locked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "worker_id": { + "name": "worker_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "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": { + "billing_reconciliation_jobs_live_checkout_uidx": { + "name": "billing_reconciliation_jobs_live_checkout_uidx", + "columns": [ + { + "expression": "checkout_attempt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_reconciliation_jobs\".\"checkout_attempt_id\" IS NOT NULL AND \"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_reconciliation_jobs_live_plan_change_uidx": { + "name": "billing_reconciliation_jobs_live_plan_change_uidx", + "columns": [ + { + "expression": "plan_change_attempt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_reconciliation_jobs\".\"plan_change_attempt_id\" IS NOT NULL AND \"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_reconciliation_jobs_live_subscription_uidx": { + "name": "billing_reconciliation_jobs_live_subscription_uidx", + "columns": [ + { + "expression": "subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_reconciliation_jobs\".\"subscription_id\" IS NOT NULL AND \"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_reconciliation_jobs_live_customer_uidx": { + "name": "billing_reconciliation_jobs_live_customer_uidx", + "columns": [ + { + "expression": "provider_customer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_reconciliation_jobs\".\"provider_customer_id\" IS NOT NULL AND \"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_reconciliation_jobs_checkout_attempt_id_billing_checkout_attempts_id_fk": { + "name": "billing_reconciliation_jobs_checkout_attempt_id_billing_checkout_attempts_id_fk", + "tableFrom": "billing_reconciliation_jobs", + "tableTo": "billing_checkout_attempts", + "columnsFrom": [ + "checkout_attempt_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_reconciliation_jobs_plan_change_attempt_id_billing_plan_change_attempts_id_fk": { + "name": "billing_reconciliation_jobs_plan_change_attempt_id_billing_plan_change_attempts_id_fk", + "tableFrom": "billing_reconciliation_jobs", + "tableTo": "billing_plan_change_attempts", + "columnsFrom": [ + "plan_change_attempt_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_reconciliation_jobs_subscription_id_billing_subscriptions_id_fk": { + "name": "billing_reconciliation_jobs_subscription_id_billing_subscriptions_id_fk", + "tableFrom": "billing_reconciliation_jobs", + "tableTo": "billing_subscriptions", + "columnsFrom": [ + "subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_reconciliation_jobs_provider_customer_id_billing_provider_customers_id_fk": { + "name": "billing_reconciliation_jobs_provider_customer_id_billing_provider_customers_id_fk", + "tableFrom": "billing_reconciliation_jobs", + "tableTo": "billing_provider_customers", + "columnsFrom": [ + "provider_customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_reconciliation_jobs_exactly_one_subject": { + "name": "billing_reconciliation_jobs_exactly_one_subject", + "value": "((CASE WHEN \"billing_reconciliation_jobs\".\"checkout_attempt_id\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"billing_reconciliation_jobs\".\"plan_change_attempt_id\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"billing_reconciliation_jobs\".\"subscription_id\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"billing_reconciliation_jobs\".\"provider_customer_id\" IS NOT NULL THEN 1 ELSE 0 END)) = 1" + }, + "billing_reconciliation_jobs_status_check": { + "name": "billing_reconciliation_jobs_status_check", + "value": "\"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed', 'completed', 'quarantined')" + }, + "billing_reconciliation_jobs_operation_check": { + "name": "billing_reconciliation_jobs_operation_check", + "value": "\"billing_reconciliation_jobs\".\"operation\" = 'reconcile' OR (\"billing_reconciliation_jobs\".\"operation\" = 'cancellation' AND \"billing_reconciliation_jobs\".\"subscription_id\" IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.billing_subscriptions": { + "name": "billing_subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "billable_entity_id": { + "name": "billable_entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_customer_id": { + "name": "billing_customer_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "payer_id": { + "name": "payer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "origin_checkout_attempt_id": { + "name": "origin_checkout_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_subscription_id": { + "name": "provider_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_product_id": { + "name": "provider_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_price_entry_id": { + "name": "billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "catalog_revision": { + "name": "catalog_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "offer_key": { + "name": "offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_interval": { + "name": "billing_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "current_period_starts_at": { + "name": "current_period_starts_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "current_period_ends_at": { + "name": "current_period_ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "paid_through_at": { + "name": "paid_through_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "trial_ends_at": { + "name": "trial_ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cancel_at_period_end": { + "name": "cancel_at_period_end", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_entitlement_source": { + "name": "is_entitlement_source", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "provider_occurred_at": { + "name": "provider_occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "provider_version": { + "name": "provider_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_observed_at": { + "name": "last_observed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_reconciled_at": { + "name": "last_reconciled_at", + "type": "timestamp with time zone", + "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": { + "billing_subscriptions_provider_subscription_uidx": { + "name": "billing_subscriptions_provider_subscription_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_subscriptions_entity_source_uidx": { + "name": "billing_subscriptions_entity_source_uidx", + "columns": [ + { + "expression": "billable_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_subscriptions\".\"is_entitlement_source\" = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_subscriptions_billable_entity_id_user_id_fk": { + "name": "billing_subscriptions_billable_entity_id_user_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "user", + "columnsFrom": [ + "billable_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_subscriptions_billing_customer_id_billing_provider_customers_id_fk": { + "name": "billing_subscriptions_billing_customer_id_billing_provider_customers_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "billing_provider_customers", + "columnsFrom": [ + "billing_customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_subscriptions_payer_id_user_id_fk": { + "name": "billing_subscriptions_payer_id_user_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "user", + "columnsFrom": [ + "payer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_subscriptions_origin_checkout_attempt_id_billing_checkout_attempts_id_fk": { + "name": "billing_subscriptions_origin_checkout_attempt_id_billing_checkout_attempts_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "billing_checkout_attempts", + "columnsFrom": [ + "origin_checkout_attempt_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_subscriptions_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_subscriptions_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "billing_price_entries", + "columnsFrom": [ + "billing_price_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_subscriptions_status_check": { + "name": "billing_subscriptions_status_check", + "value": "\"billing_subscriptions\".\"status\" IN ('pending', 'trialing', 'active', 'past_due', 'cancelled', 'expired')" + }, + "billing_subscriptions_plan_check": { + "name": "billing_subscriptions_plan_check", + "value": "\"billing_subscriptions\".\"plan\" IN ('pro')" + }, + "billing_subscriptions_interval_check": { + "name": "billing_subscriptions_interval_check", + "value": "\"billing_subscriptions\".\"billing_interval\" IN ('month', 'year')" + } + }, + "isRLSEnabled": false + }, + "public.billing_webhook_events": { + "name": "billing_webhook_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_event_id": { + "name": "provider_event_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "subscription_id": { + "name": "subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "checkout_attempt_id": { + "name": "checkout_attempt_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload_encrypted": { + "name": "payload_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload_key_version": { + "name": "payload_key_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "verified_key_version": { + "name": "verified_key_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "processing_attempts": { + "name": "processing_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "available_at": { + "name": "available_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "locked_at": { + "name": "locked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "worker_id": { + "name": "worker_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "received_at": { + "name": "received_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "billing_webhook_events_provider_event_uidx": { + "name": "billing_webhook_events_provider_event_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_webhook_events_queue_idx": { + "name": "billing_webhook_events_queue_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "available_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_webhook_events_status_check": { + "name": "billing_webhook_events_status_check", + "value": "\"billing_webhook_events\".\"status\" IN ('pending', 'processing', 'processed', 'ignored', 'quarantined', 'failed')" + } + }, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/api/drizzle/meta/0005_snapshot.json b/apps/api/drizzle/meta/0005_snapshot.json new file mode 100644 index 00000000..91a49620 --- /dev/null +++ b/apps/api/drizzle/meta/0005_snapshot.json @@ -0,0 +1,3801 @@ +{ + "id": "d2cb6b52-ce3d-41c3-97c7-092a9d1504a7", + "prevId": "a85ef515-4345-4e69-b2fd-39fa78d427ac", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_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 + }, + "id_token": { + "name": "id_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 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.jwks": { + "name": "jwks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "alg": { + "name": "alg", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "crv": { + "name": "crv", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_access_token": { + "name": "oauth_access_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorization_code_id": { + "name": "authorization_code_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "refresh_id": { + "name": "refresh_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked": { + "name": "revoked", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "confirmation": { + "name": "confirmation", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_access_token_client_id_oauth_client_client_id_fk": { + "name": "oauth_access_token_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_access_token", + "columnsFrom": [ + "client_id" + ], + "tableTo": "oauth_client", + "columnsTo": [ + "client_id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "oauth_access_token_session_id_session_id_fk": { + "name": "oauth_access_token_session_id_session_id_fk", + "tableFrom": "oauth_access_token", + "columnsFrom": [ + "session_id" + ], + "tableTo": "session", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "oauth_access_token_user_id_user_id_fk": { + "name": "oauth_access_token_user_id_user_id_fk", + "tableFrom": "oauth_access_token", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "oauth_access_token_refresh_id_oauth_refresh_token_id_fk": { + "name": "oauth_access_token_refresh_id_oauth_refresh_token_id_fk", + "tableFrom": "oauth_access_token", + "columnsFrom": [ + "refresh_id" + ], + "tableTo": "oauth_refresh_token", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_access_token_token_unique": { + "name": "oauth_access_token_token_unique", + "columns": [ + "token" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_client": { + "name": "oauth_client", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_discovery_id": { + "name": "client_discovery_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "skip_consent": { + "name": "skip_consent", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "enable_end_session": { + "name": "enable_end_session", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subject_type": { + "name": "subject_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "client_credentials_scopes": { + "name": "client_credentials_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contacts": { + "name": "contacts", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "tos": { + "name": "tos", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "policy": { + "name": "policy", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_id": { + "name": "software_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_version": { + "name": "software_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_statement": { + "name": "software_statement", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uris": { + "name": "redirect_uris", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "post_logout_redirect_uris": { + "name": "post_logout_redirect_uris", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "backchannel_logout_uri": { + "name": "backchannel_logout_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "backchannel_logout_session_required": { + "name": "backchannel_logout_session_required", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "token_endpoint_auth_method": { + "name": "token_endpoint_auth_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "application_type": { + "name": "application_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "jwks": { + "name": "jwks", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "jwks_uri": { + "name": "jwks_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "grant_types": { + "name": "grant_types", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "response_types": { + "name": "response_types", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "require_pkce": { + "name": "require_pkce", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "dpop_bound_access_tokens": { + "name": "dpop_bound_access_tokens", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_client_user_id_user_id_fk": { + "name": "oauth_client_user_id_user_id_fk", + "tableFrom": "oauth_client", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_client_client_id_unique": { + "name": "oauth_client_client_id_unique", + "columns": [ + "client_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_client_assertion": { + "name": "oauth_client_assertion", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_client_resource": { + "name": "oauth_client_resource", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_client_resource_client_id_oauth_client_client_id_fk": { + "name": "oauth_client_resource_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_client_resource", + "columnsFrom": [ + "client_id" + ], + "tableTo": "oauth_client", + "columnsTo": [ + "client_id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "oauth_client_resource_resource_id_oauth_resource_identifier_fk": { + "name": "oauth_client_resource_resource_id_oauth_resource_identifier_fk", + "tableFrom": "oauth_client_resource", + "columnsFrom": [ + "resource_id" + ], + "tableTo": "oauth_resource", + "columnsTo": [ + "identifier" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_consent": { + "name": "oauth_consent", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_consent_client_id_oauth_client_client_id_fk": { + "name": "oauth_consent_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_consent", + "columnsFrom": [ + "client_id" + ], + "tableTo": "oauth_client", + "columnsTo": [ + "client_id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "oauth_consent_user_id_user_id_fk": { + "name": "oauth_consent_user_id_user_id_fk", + "tableFrom": "oauth_consent", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_refresh_token": { + "name": "oauth_refresh_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorization_code_id": { + "name": "authorization_code_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked": { + "name": "revoked", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "rotated_at": { + "name": "rotated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "rotation_replay_response": { + "name": "rotation_replay_response", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rotation_replay_expires_at": { + "name": "rotation_replay_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "auth_time": { + "name": "auth_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "confirmation": { + "name": "confirmation", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_refresh_token_client_id_oauth_client_client_id_fk": { + "name": "oauth_refresh_token_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_refresh_token", + "columnsFrom": [ + "client_id" + ], + "tableTo": "oauth_client", + "columnsTo": [ + "client_id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "oauth_refresh_token_session_id_session_id_fk": { + "name": "oauth_refresh_token_session_id_session_id_fk", + "tableFrom": "oauth_refresh_token", + "columnsFrom": [ + "session_id" + ], + "tableTo": "session", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "oauth_refresh_token_user_id_user_id_fk": { + "name": "oauth_refresh_token_user_id_user_id_fk", + "tableFrom": "oauth_refresh_token", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_refresh_token_token_unique": { + "name": "oauth_refresh_token_token_unique", + "columns": [ + "token" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_resource": { + "name": "oauth_resource", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token_ttl": { + "name": "access_token_ttl", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "refresh_token_ttl": { + "name": "refresh_token_ttl", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "signing_algorithm": { + "name": "signing_algorithm", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "signing_key_id": { + "name": "signing_key_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "allowed_scopes": { + "name": "allowed_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "custom_claims": { + "name": "custom_claims", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "dpop_bound_access_tokens_required": { + "name": "dpop_bound_access_tokens_required", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "policy_version": { + "name": "policy_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_resource_identifier_unique": { + "name": "oauth_resource_identifier_unique", + "columns": [ + "identifier" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_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 + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "columns": [ + "token" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "columns": [ + "email" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "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 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.api_keys": { + "name": "api_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "key_id": { + "name": "key_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "restriction": { + "name": "restriction", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_referrers": { + "name": "http_referrers", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "ip_addresses": { + "name": "ip_addresses", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "api_keys_user_name_uidx": { + "name": "api_keys_user_name_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"api_keys\".\"deleted\" = false", + "concurrently": false + }, + "api_keys_one_default_uidx": { + "name": "api_keys_one_default_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"api_keys\".\"is_default\" = true AND \"api_keys\".\"deleted\" = false", + "concurrently": false + } + }, + "foreignKeys": { + "api_keys_user_id_user_id_fk": { + "name": "api_keys_user_id_user_id_fk", + "tableFrom": "api_keys", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_keys_key_id_unique": { + "name": "api_keys_key_id_unique", + "columns": [ + "key_id" + ], + "nullsNotDistinct": false + }, + "api_keys_key_unique": { + "name": "api_keys_key_unique", + "columns": [ + "key" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.media": { + "name": "media", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media_id": { + "name": "media_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apikey": { + "name": "apikey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "original_file_name": { + "name": "original_file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "thumbnail_generated": { + "name": "thumbnail_generated", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "access_control": { + "name": "access_control", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'private'" + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "caption": { + "name": "caption", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "temp": { + "name": "temp", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "media_user_id_user_id_fk": { + "name": "media_user_id_user_id_fk", + "tableFrom": "media", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "media_media_id_unique": { + "name": "media_media_id_unique", + "columns": [ + "media_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.media_settings": { + "name": "media_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apikey": { + "name": "apikey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "use_web_p": { + "name": "use_web_p", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "webp_output_quality": { + "name": "webp_output_quality", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "thumbnail_width": { + "name": "thumbnail_width", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "thumbnail_height": { + "name": "thumbnail_height", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "media_settings_user_id_user_id_fk": { + "name": "media_settings_user_id_user_id_fk", + "tableFrom": "media_settings", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "media_settings_apikey_unique": { + "name": "media_settings_apikey_unique", + "columns": [ + "apikey" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.profiles": { + "name": "profiles", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "public_user_id": { + "name": "public_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subscription_id": { + "name": "subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subscription_ends_after": { + "name": "subscription_ends_after", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "subscription_method": { + "name": "subscription_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subscription_status": { + "name": "subscription_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'not-subscribed'" + } + }, + "indexes": {}, + "foreignKeys": { + "profiles_user_id_user_id_fk": { + "name": "profiles_user_id_user_id_fk", + "tableFrom": "profiles", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "profiles_public_user_id_unique": { + "name": "profiles_public_user_id_unique", + "columns": [ + "public_user_id" + ], + "nullsNotDistinct": false + }, + "profiles_customer_id_unique": { + "name": "profiles_customer_id_unique", + "columns": [ + "customer_id" + ], + "nullsNotDistinct": false + }, + "profiles_subscription_id_unique": { + "name": "profiles_subscription_id_unique", + "columns": [ + "subscription_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.signatures": { + "name": "signatures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apikey": { + "name": "apikey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "signature": { + "name": "signature", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "valid_till": { + "name": "valid_till", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "signatures_signature_unique": { + "name": "signatures_signature_unique", + "columns": [ + "signature" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tus_uploads": { + "name": "tus_uploads", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "upload_id": { + "name": "upload_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apikey": { + "name": "apikey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "upload_length": { + "name": "upload_length", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "upload_offset": { + "name": "upload_offset", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "temp_file_path": { + "name": "temp_file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "signature": { + "name": "signature", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_complete": { + "name": "is_complete", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "tus_uploads_upload_id_unique": { + "name": "tus_uploads_upload_id_unique", + "columns": [ + "upload_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.billing_catalog_revision_items": { + "name": "billing_catalog_revision_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "catalog_revision_id": { + "name": "catalog_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "offer_key": { + "name": "offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_price_entry_id": { + "name": "billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "billing_catalog_revision_items_revision_key_uidx": { + "name": "billing_catalog_revision_items_revision_key_uidx", + "columns": [ + { + "expression": "catalog_revision_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "offer_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "billing_catalog_revision_items_revision_price_uidx": { + "name": "billing_catalog_revision_items_revision_price_uidx", + "columns": [ + { + "expression": "catalog_revision_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "billing_price_entry_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "billing_catalog_revision_items_catalog_revision_id_billing_catalog_revisions_id_fk": { + "name": "billing_catalog_revision_items_catalog_revision_id_billing_catalog_revisions_id_fk", + "tableFrom": "billing_catalog_revision_items", + "columnsFrom": [ + "catalog_revision_id" + ], + "tableTo": "billing_catalog_revisions", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "billing_catalog_revision_items_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_catalog_revision_items_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_catalog_revision_items", + "columnsFrom": [ + "billing_price_entry_id" + ], + "tableTo": "billing_price_entries", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.billing_catalog_revisions": { + "name": "billing_catalog_revisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "checkout_provider": { + "name": "checkout_provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending_verification'" + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "activated_at": { + "name": "activated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "retired_at": { + "name": "retired_at", + "type": "timestamp with time zone", + "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": { + "billing_catalog_revisions_active_provider_uidx": { + "name": "billing_catalog_revisions_active_provider_uidx", + "columns": [ + { + "expression": "checkout_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"billing_catalog_revisions\".\"status\" = 'active'", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "billing_catalog_revisions_revision_unique": { + "name": "billing_catalog_revisions_revision_unique", + "columns": [ + "revision" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": { + "billing_catalog_revisions_status_check": { + "name": "billing_catalog_revisions_status_check", + "value": "\"billing_catalog_revisions\".\"status\" IN ('pending_verification', 'active', 'retired', 'invalid', 'abandoned')" + }, + "billing_catalog_revisions_revision_check": { + "name": "billing_catalog_revisions_revision_check", + "value": "\"billing_catalog_revisions\".\"revision\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.billing_checkout_attempts": { + "name": "billing_checkout_attempts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "attempt_id": { + "name": "attempt_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billable_entity_id": { + "name": "billable_entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_id": { + "name": "payer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_email": { + "name": "payer_email", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "return_url": { + "name": "return_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "catalog_revision": { + "name": "catalog_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "offer_key": { + "name": "offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_plan": { + "name": "requested_plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_interval": { + "name": "requested_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_price_entry_id": { + "name": "billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "quoted_amount_minor": { + "name": "quoted_amount_minor", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "quoted_currency": { + "name": "quoted_currency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_customer_id": { + "name": "billing_customer_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider_checkout_session_id": { + "name": "provider_checkout_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "checkout_url_encrypted": { + "name": "checkout_url_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'creating'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "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": { + "billing_checkout_attempts_provider_session_uidx": { + "name": "billing_checkout_attempts_provider_session_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_checkout_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"billing_checkout_attempts\".\"provider_checkout_session_id\" IS NOT NULL", + "concurrently": false + }, + "billing_checkout_attempts_idempotency_uidx": { + "name": "billing_checkout_attempts_idempotency_uidx", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "billing_checkout_attempts_entity_nonterminal_uidx": { + "name": "billing_checkout_attempts_entity_nonterminal_uidx", + "columns": [ + { + "expression": "billable_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"billing_checkout_attempts\".\"status\" IN ('creating', 'open')", + "concurrently": false + } + }, + "foreignKeys": { + "billing_checkout_attempts_billable_entity_id_user_id_fk": { + "name": "billing_checkout_attempts_billable_entity_id_user_id_fk", + "tableFrom": "billing_checkout_attempts", + "columnsFrom": [ + "billable_entity_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "billing_checkout_attempts_payer_id_user_id_fk": { + "name": "billing_checkout_attempts_payer_id_user_id_fk", + "tableFrom": "billing_checkout_attempts", + "columnsFrom": [ + "payer_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "billing_checkout_attempts_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_checkout_attempts_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_checkout_attempts", + "columnsFrom": [ + "billing_price_entry_id" + ], + "tableTo": "billing_price_entries", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "billing_checkout_attempts_billing_customer_id_billing_provider_customers_id_fk": { + "name": "billing_checkout_attempts_billing_customer_id_billing_provider_customers_id_fk", + "tableFrom": "billing_checkout_attempts", + "columnsFrom": [ + "billing_customer_id" + ], + "tableTo": "billing_provider_customers", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "billing_checkout_attempts_attempt_id_unique": { + "name": "billing_checkout_attempts_attempt_id_unique", + "columns": [ + "attempt_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": { + "billing_checkout_attempts_status_check": { + "name": "billing_checkout_attempts_status_check", + "value": "\"billing_checkout_attempts\".\"status\" IN ('creating', 'open', 'completed', 'expired', 'abandoned', 'conflicted')" + }, + "billing_checkout_attempts_amount_check": { + "name": "billing_checkout_attempts_amount_check", + "value": "\"billing_checkout_attempts\".\"quoted_amount_minor\" > 0" + }, + "billing_checkout_attempts_plan_check": { + "name": "billing_checkout_attempts_plan_check", + "value": "\"billing_checkout_attempts\".\"requested_plan\" IN ('pro')" + }, + "billing_checkout_attempts_interval_check": { + "name": "billing_checkout_attempts_interval_check", + "value": "\"billing_checkout_attempts\".\"requested_interval\" IN ('month', 'year')" + } + }, + "isRLSEnabled": false + }, + "public.billing_plan_change_attempts": { + "name": "billing_plan_change_attempts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "change_id": { + "name": "change_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billable_entity_id": { + "name": "billable_entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subscription_id": { + "name": "subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_id": { + "name": "payer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "current_catalog_revision": { + "name": "current_catalog_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "current_billing_price_entry_id": { + "name": "current_billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "current_plan": { + "name": "current_plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "current_interval": { + "name": "current_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_catalog_revision": { + "name": "target_catalog_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "target_billing_price_entry_id": { + "name": "target_billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "target_plan": { + "name": "target_plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_interval": { + "name": "target_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_offer_key": { + "name": "target_offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "effective_at": { + "name": "effective_at", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "proration_mode": { + "name": "proration_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_payment_id": { + "name": "provider_payment_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payment_url_encrypted": { + "name": "payment_url_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'creating'" + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "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": { + "billing_plan_change_attempts_idempotency_uidx": { + "name": "billing_plan_change_attempts_idempotency_uidx", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "billing_plan_change_attempts_entity_nonterminal_uidx": { + "name": "billing_plan_change_attempts_entity_nonterminal_uidx", + "columns": [ + { + "expression": "billable_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"billing_plan_change_attempts\".\"status\" IN ('creating', 'pending')", + "concurrently": false + } + }, + "foreignKeys": { + "billing_plan_change_attempts_billable_entity_id_user_id_fk": { + "name": "billing_plan_change_attempts_billable_entity_id_user_id_fk", + "tableFrom": "billing_plan_change_attempts", + "columnsFrom": [ + "billable_entity_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "billing_plan_change_attempts_subscription_id_billing_subscriptions_id_fk": { + "name": "billing_plan_change_attempts_subscription_id_billing_subscriptions_id_fk", + "tableFrom": "billing_plan_change_attempts", + "columnsFrom": [ + "subscription_id" + ], + "tableTo": "billing_subscriptions", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "billing_plan_change_attempts_payer_id_user_id_fk": { + "name": "billing_plan_change_attempts_payer_id_user_id_fk", + "tableFrom": "billing_plan_change_attempts", + "columnsFrom": [ + "payer_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "billing_plan_change_attempts_current_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_plan_change_attempts_current_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_plan_change_attempts", + "columnsFrom": [ + "current_billing_price_entry_id" + ], + "tableTo": "billing_price_entries", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "billing_plan_change_attempts_target_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_plan_change_attempts_target_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_plan_change_attempts", + "columnsFrom": [ + "target_billing_price_entry_id" + ], + "tableTo": "billing_price_entries", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "billing_plan_change_attempts_change_id_unique": { + "name": "billing_plan_change_attempts_change_id_unique", + "columns": [ + "change_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": { + "billing_plan_change_attempts_status_check": { + "name": "billing_plan_change_attempts_status_check", + "value": "\"billing_plan_change_attempts\".\"status\" IN ('creating', 'pending', 'succeeded', 'failed', 'conflicted')" + }, + "billing_plan_change_attempts_effective_at_check": { + "name": "billing_plan_change_attempts_effective_at_check", + "value": "\"billing_plan_change_attempts\".\"effective_at\" IN ('immediately', 'next_billing_date')" + }, + "billing_plan_change_attempts_proration_mode_check": { + "name": "billing_plan_change_attempts_proration_mode_check", + "value": "\"billing_plan_change_attempts\".\"proration_mode\" IN ('prorated_immediately', 'do_not_bill')" + }, + "billing_plan_change_attempts_current_plan_check": { + "name": "billing_plan_change_attempts_current_plan_check", + "value": "\"billing_plan_change_attempts\".\"current_plan\" IN ('pro')" + }, + "billing_plan_change_attempts_target_plan_check": { + "name": "billing_plan_change_attempts_target_plan_check", + "value": "\"billing_plan_change_attempts\".\"target_plan\" IN ('pro')" + } + }, + "isRLSEnabled": false + }, + "public.billing_plan_states": { + "name": "billing_plan_states", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "billable_entity_id": { + "name": "billable_entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active_subscription_id": { + "name": "active_subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "projection_version": { + "name": "projection_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "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": { + "billing_plan_states_billable_entity_id_user_id_fk": { + "name": "billing_plan_states_billable_entity_id_user_id_fk", + "tableFrom": "billing_plan_states", + "columnsFrom": [ + "billable_entity_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "billing_plan_states_active_subscription_id_billing_subscriptions_id_fk": { + "name": "billing_plan_states_active_subscription_id_billing_subscriptions_id_fk", + "tableFrom": "billing_plan_states", + "columnsFrom": [ + "active_subscription_id" + ], + "tableTo": "billing_subscriptions", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "billing_plan_states_billable_entity_id_unique": { + "name": "billing_plan_states_billable_entity_id_unique", + "columns": [ + "billable_entity_id" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.billing_price_entries": { + "name": "billing_price_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "offer_key": { + "name": "offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_interval": { + "name": "billing_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount_minor": { + "name": "amount_minor", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "provider_trial_days": { + "name": "provider_trial_days", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_product_id": { + "name": "provider_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp with time zone", + "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": { + "billing_price_entries_provider_product_uidx": { + "name": "billing_price_entries_provider_product_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "billing_price_entries_offer_key_idx": { + "name": "billing_price_entries_offer_key_idx", + "columns": [ + { + "expression": "offer_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_price_entries_amount_check": { + "name": "billing_price_entries_amount_check", + "value": "\"billing_price_entries\".\"amount_minor\" > 0" + }, + "billing_price_entries_trial_days_check": { + "name": "billing_price_entries_trial_days_check", + "value": "\"billing_price_entries\".\"provider_trial_days\" >= 0" + }, + "billing_price_entries_currency_check": { + "name": "billing_price_entries_currency_check", + "value": "\"billing_price_entries\".\"currency\" ~ '^[A-Z]{3}$'" + }, + "billing_price_entries_plan_check": { + "name": "billing_price_entries_plan_check", + "value": "\"billing_price_entries\".\"plan\" IN ('pro')" + }, + "billing_price_entries_interval_check": { + "name": "billing_price_entries_interval_check", + "value": "\"billing_price_entries\".\"billing_interval\" IN ('month', 'year')" + } + }, + "isRLSEnabled": false + }, + "public.billing_provider_customers": { + "name": "billing_provider_customers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_id": { + "name": "payer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_email": { + "name": "payer_email", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "provider_customer_id": { + "name": "provider_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'creating'" + }, + "last_error": { + "name": "last_error", + "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": { + "billing_provider_customers_provider_payer_uidx": { + "name": "billing_provider_customers_provider_payer_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "payer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "billing_provider_customers_provider_customer_uidx": { + "name": "billing_provider_customers_provider_customer_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_customer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"billing_provider_customers\".\"provider_customer_id\" IS NOT NULL", + "concurrently": false + }, + "billing_provider_customers_idempotency_uidx": { + "name": "billing_provider_customers_idempotency_uidx", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "billing_provider_customers_payer_id_user_id_fk": { + "name": "billing_provider_customers_payer_id_user_id_fk", + "tableFrom": "billing_provider_customers", + "columnsFrom": [ + "payer_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_provider_customers_status_check": { + "name": "billing_provider_customers_status_check", + "value": "\"billing_provider_customers\".\"status\" IN ('creating', 'active', 'conflicted')" + } + }, + "isRLSEnabled": false + }, + "public.billing_reconciliation_jobs": { + "name": "billing_reconciliation_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "checkout_attempt_id": { + "name": "checkout_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "plan_change_attempt_id": { + "name": "plan_change_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "subscription_id": { + "name": "subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider_customer_id": { + "name": "provider_customer_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "operation": { + "name": "operation", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'reconcile'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "available_at": { + "name": "available_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "locked_at": { + "name": "locked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "worker_id": { + "name": "worker_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "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": { + "billing_reconciliation_jobs_live_checkout_uidx": { + "name": "billing_reconciliation_jobs_live_checkout_uidx", + "columns": [ + { + "expression": "checkout_attempt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"billing_reconciliation_jobs\".\"checkout_attempt_id\" IS NOT NULL AND \"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed')", + "concurrently": false + }, + "billing_reconciliation_jobs_live_plan_change_uidx": { + "name": "billing_reconciliation_jobs_live_plan_change_uidx", + "columns": [ + { + "expression": "plan_change_attempt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"billing_reconciliation_jobs\".\"plan_change_attempt_id\" IS NOT NULL AND \"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed')", + "concurrently": false + }, + "billing_reconciliation_jobs_live_subscription_uidx": { + "name": "billing_reconciliation_jobs_live_subscription_uidx", + "columns": [ + { + "expression": "subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"billing_reconciliation_jobs\".\"subscription_id\" IS NOT NULL AND \"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed')", + "concurrently": false + }, + "billing_reconciliation_jobs_live_customer_uidx": { + "name": "billing_reconciliation_jobs_live_customer_uidx", + "columns": [ + { + "expression": "provider_customer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"billing_reconciliation_jobs\".\"provider_customer_id\" IS NOT NULL AND \"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed')", + "concurrently": false + } + }, + "foreignKeys": { + "billing_reconciliation_jobs_checkout_attempt_id_billing_checkout_attempts_id_fk": { + "name": "billing_reconciliation_jobs_checkout_attempt_id_billing_checkout_attempts_id_fk", + "tableFrom": "billing_reconciliation_jobs", + "columnsFrom": [ + "checkout_attempt_id" + ], + "tableTo": "billing_checkout_attempts", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "billing_reconciliation_jobs_plan_change_attempt_id_billing_plan_change_attempts_id_fk": { + "name": "billing_reconciliation_jobs_plan_change_attempt_id_billing_plan_change_attempts_id_fk", + "tableFrom": "billing_reconciliation_jobs", + "columnsFrom": [ + "plan_change_attempt_id" + ], + "tableTo": "billing_plan_change_attempts", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "billing_reconciliation_jobs_subscription_id_billing_subscriptions_id_fk": { + "name": "billing_reconciliation_jobs_subscription_id_billing_subscriptions_id_fk", + "tableFrom": "billing_reconciliation_jobs", + "columnsFrom": [ + "subscription_id" + ], + "tableTo": "billing_subscriptions", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "billing_reconciliation_jobs_provider_customer_id_billing_provider_customers_id_fk": { + "name": "billing_reconciliation_jobs_provider_customer_id_billing_provider_customers_id_fk", + "tableFrom": "billing_reconciliation_jobs", + "columnsFrom": [ + "provider_customer_id" + ], + "tableTo": "billing_provider_customers", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_reconciliation_jobs_exactly_one_subject": { + "name": "billing_reconciliation_jobs_exactly_one_subject", + "value": "((CASE WHEN \"billing_reconciliation_jobs\".\"checkout_attempt_id\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"billing_reconciliation_jobs\".\"plan_change_attempt_id\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"billing_reconciliation_jobs\".\"subscription_id\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"billing_reconciliation_jobs\".\"provider_customer_id\" IS NOT NULL THEN 1 ELSE 0 END)) = 1" + }, + "billing_reconciliation_jobs_status_check": { + "name": "billing_reconciliation_jobs_status_check", + "value": "\"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed', 'completed', 'quarantined')" + }, + "billing_reconciliation_jobs_operation_check": { + "name": "billing_reconciliation_jobs_operation_check", + "value": "\"billing_reconciliation_jobs\".\"operation\" = 'reconcile' OR (\"billing_reconciliation_jobs\".\"operation\" = 'cancellation' AND \"billing_reconciliation_jobs\".\"subscription_id\" IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.billing_subscriptions": { + "name": "billing_subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "billable_entity_id": { + "name": "billable_entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_customer_id": { + "name": "billing_customer_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "payer_id": { + "name": "payer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "origin_checkout_attempt_id": { + "name": "origin_checkout_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_subscription_id": { + "name": "provider_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_product_id": { + "name": "provider_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_price_entry_id": { + "name": "billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "catalog_revision": { + "name": "catalog_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "offer_key": { + "name": "offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_interval": { + "name": "billing_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "current_period_starts_at": { + "name": "current_period_starts_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "current_period_ends_at": { + "name": "current_period_ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "paid_through_at": { + "name": "paid_through_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "trial_ends_at": { + "name": "trial_ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cancel_at_period_end": { + "name": "cancel_at_period_end", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_entitlement_source": { + "name": "is_entitlement_source", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "provider_occurred_at": { + "name": "provider_occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "provider_version": { + "name": "provider_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_observed_at": { + "name": "last_observed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_reconciled_at": { + "name": "last_reconciled_at", + "type": "timestamp with time zone", + "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": { + "billing_subscriptions_provider_subscription_uidx": { + "name": "billing_subscriptions_provider_subscription_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "billing_subscriptions_entity_source_uidx": { + "name": "billing_subscriptions_entity_source_uidx", + "columns": [ + { + "expression": "billable_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"billing_subscriptions\".\"is_entitlement_source\" = true", + "concurrently": false + } + }, + "foreignKeys": { + "billing_subscriptions_billable_entity_id_user_id_fk": { + "name": "billing_subscriptions_billable_entity_id_user_id_fk", + "tableFrom": "billing_subscriptions", + "columnsFrom": [ + "billable_entity_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "billing_subscriptions_billing_customer_id_billing_provider_customers_id_fk": { + "name": "billing_subscriptions_billing_customer_id_billing_provider_customers_id_fk", + "tableFrom": "billing_subscriptions", + "columnsFrom": [ + "billing_customer_id" + ], + "tableTo": "billing_provider_customers", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "billing_subscriptions_payer_id_user_id_fk": { + "name": "billing_subscriptions_payer_id_user_id_fk", + "tableFrom": "billing_subscriptions", + "columnsFrom": [ + "payer_id" + ], + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "billing_subscriptions_origin_checkout_attempt_id_billing_checkout_attempts_id_fk": { + "name": "billing_subscriptions_origin_checkout_attempt_id_billing_checkout_attempts_id_fk", + "tableFrom": "billing_subscriptions", + "columnsFrom": [ + "origin_checkout_attempt_id" + ], + "tableTo": "billing_checkout_attempts", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "billing_subscriptions_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_subscriptions_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_subscriptions", + "columnsFrom": [ + "billing_price_entry_id" + ], + "tableTo": "billing_price_entries", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_subscriptions_status_check": { + "name": "billing_subscriptions_status_check", + "value": "\"billing_subscriptions\".\"status\" IN ('pending', 'trialing', 'active', 'past_due', 'cancelled', 'expired')" + }, + "billing_subscriptions_plan_check": { + "name": "billing_subscriptions_plan_check", + "value": "\"billing_subscriptions\".\"plan\" IN ('pro')" + }, + "billing_subscriptions_interval_check": { + "name": "billing_subscriptions_interval_check", + "value": "\"billing_subscriptions\".\"billing_interval\" IN ('month', 'year')" + } + }, + "isRLSEnabled": false + }, + "public.billing_webhook_events": { + "name": "billing_webhook_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_event_id": { + "name": "provider_event_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "subscription_id": { + "name": "subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "checkout_attempt_id": { + "name": "checkout_attempt_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload_encrypted": { + "name": "payload_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload_key_version": { + "name": "payload_key_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "verified_key_version": { + "name": "verified_key_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "processing_attempts": { + "name": "processing_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "available_at": { + "name": "available_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "locked_at": { + "name": "locked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "worker_id": { + "name": "worker_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "received_at": { + "name": "received_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "billing_webhook_events_provider_event_uidx": { + "name": "billing_webhook_events_provider_event_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "billing_webhook_events_queue_idx": { + "name": "billing_webhook_events_queue_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "available_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_webhook_events_status_check": { + "name": "billing_webhook_events_status_check", + "value": "\"billing_webhook_events\".\"status\" IN ('pending', 'processing', 'processed', 'ignored', 'quarantined', 'failed')" + } + }, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "views": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/api/drizzle/meta/0006_snapshot.json b/apps/api/drizzle/meta/0006_snapshot.json new file mode 100644 index 00000000..627c9cef --- /dev/null +++ b/apps/api/drizzle/meta/0006_snapshot.json @@ -0,0 +1,3860 @@ +{ + "id": "7b4b0959-5485-4ce3-bcd7-32a5576dec18", + "prevId": "d2cb6b52-ce3d-41c3-97c7-092a9d1504a7", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_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 + }, + "id_token": { + "name": "id_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 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.jwks": { + "name": "jwks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "alg": { + "name": "alg", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "crv": { + "name": "crv", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_access_token": { + "name": "oauth_access_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorization_code_id": { + "name": "authorization_code_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "refresh_id": { + "name": "refresh_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked": { + "name": "revoked", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "confirmation": { + "name": "confirmation", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_access_token_client_id_oauth_client_client_id_fk": { + "name": "oauth_access_token_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_access_token_session_id_session_id_fk": { + "name": "oauth_access_token_session_id_session_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "session", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "oauth_access_token_user_id_user_id_fk": { + "name": "oauth_access_token_user_id_user_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_access_token_refresh_id_oauth_refresh_token_id_fk": { + "name": "oauth_access_token_refresh_id_oauth_refresh_token_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "oauth_refresh_token", + "columnsFrom": [ + "refresh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_access_token_token_unique": { + "name": "oauth_access_token_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_client": { + "name": "oauth_client", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_discovery_id": { + "name": "client_discovery_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "skip_consent": { + "name": "skip_consent", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "enable_end_session": { + "name": "enable_end_session", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subject_type": { + "name": "subject_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "client_credentials_scopes": { + "name": "client_credentials_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contacts": { + "name": "contacts", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "tos": { + "name": "tos", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "policy": { + "name": "policy", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_id": { + "name": "software_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_version": { + "name": "software_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_statement": { + "name": "software_statement", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uris": { + "name": "redirect_uris", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "post_logout_redirect_uris": { + "name": "post_logout_redirect_uris", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "backchannel_logout_uri": { + "name": "backchannel_logout_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "backchannel_logout_session_required": { + "name": "backchannel_logout_session_required", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "token_endpoint_auth_method": { + "name": "token_endpoint_auth_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "application_type": { + "name": "application_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "jwks": { + "name": "jwks", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "jwks_uri": { + "name": "jwks_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "grant_types": { + "name": "grant_types", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "response_types": { + "name": "response_types", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "require_pkce": { + "name": "require_pkce", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "dpop_bound_access_tokens": { + "name": "dpop_bound_access_tokens", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_client_user_id_user_id_fk": { + "name": "oauth_client_user_id_user_id_fk", + "tableFrom": "oauth_client", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_client_client_id_unique": { + "name": "oauth_client_client_id_unique", + "nullsNotDistinct": false, + "columns": [ + "client_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_client_assertion": { + "name": "oauth_client_assertion", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_client_resource": { + "name": "oauth_client_resource", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_client_resource_client_id_oauth_client_client_id_fk": { + "name": "oauth_client_resource_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_client_resource", + "tableTo": "oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_client_resource_resource_id_oauth_resource_identifier_fk": { + "name": "oauth_client_resource_resource_id_oauth_resource_identifier_fk", + "tableFrom": "oauth_client_resource", + "tableTo": "oauth_resource", + "columnsFrom": [ + "resource_id" + ], + "columnsTo": [ + "identifier" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_consent": { + "name": "oauth_consent", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_consent_client_id_oauth_client_client_id_fk": { + "name": "oauth_consent_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_consent", + "tableTo": "oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_consent_user_id_user_id_fk": { + "name": "oauth_consent_user_id_user_id_fk", + "tableFrom": "oauth_consent", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_refresh_token": { + "name": "oauth_refresh_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorization_code_id": { + "name": "authorization_code_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked": { + "name": "revoked", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "rotated_at": { + "name": "rotated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "rotation_replay_response": { + "name": "rotation_replay_response", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rotation_replay_expires_at": { + "name": "rotation_replay_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "auth_time": { + "name": "auth_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "confirmation": { + "name": "confirmation", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_refresh_token_client_id_oauth_client_client_id_fk": { + "name": "oauth_refresh_token_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_refresh_token", + "tableTo": "oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_refresh_token_session_id_session_id_fk": { + "name": "oauth_refresh_token_session_id_session_id_fk", + "tableFrom": "oauth_refresh_token", + "tableTo": "session", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "oauth_refresh_token_user_id_user_id_fk": { + "name": "oauth_refresh_token_user_id_user_id_fk", + "tableFrom": "oauth_refresh_token", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_refresh_token_token_unique": { + "name": "oauth_refresh_token_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_resource": { + "name": "oauth_resource", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token_ttl": { + "name": "access_token_ttl", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "refresh_token_ttl": { + "name": "refresh_token_ttl", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "signing_algorithm": { + "name": "signing_algorithm", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "signing_key_id": { + "name": "signing_key_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "allowed_scopes": { + "name": "allowed_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "custom_claims": { + "name": "custom_claims", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "dpop_bound_access_tokens_required": { + "name": "dpop_bound_access_tokens_required", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "policy_version": { + "name": "policy_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_resource_identifier_unique": { + "name": "oauth_resource_identifier_unique", + "nullsNotDistinct": false, + "columns": [ + "identifier" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_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 + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "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 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.api_keys": { + "name": "api_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "key_id": { + "name": "key_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "restriction": { + "name": "restriction", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_referrers": { + "name": "http_referrers", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "ip_addresses": { + "name": "ip_addresses", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "api_keys_user_name_uidx": { + "name": "api_keys_user_name_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"api_keys\".\"deleted\" = false", + "concurrently": false, + "method": "btree", + "with": {} + }, + "api_keys_one_default_uidx": { + "name": "api_keys_one_default_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"api_keys\".\"is_default\" = true AND \"api_keys\".\"deleted\" = false", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "api_keys_user_id_user_id_fk": { + "name": "api_keys_user_id_user_id_fk", + "tableFrom": "api_keys", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_keys_key_id_unique": { + "name": "api_keys_key_id_unique", + "nullsNotDistinct": false, + "columns": [ + "key_id" + ] + }, + "api_keys_key_unique": { + "name": "api_keys_key_unique", + "nullsNotDistinct": false, + "columns": [ + "key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.media": { + "name": "media", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "media_id": { + "name": "media_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apikey": { + "name": "apikey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "original_file_name": { + "name": "original_file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "thumbnail_generated": { + "name": "thumbnail_generated", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "access_control": { + "name": "access_control", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'private'" + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "caption": { + "name": "caption", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "temp": { + "name": "temp", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "media_user_id_user_id_fk": { + "name": "media_user_id_user_id_fk", + "tableFrom": "media", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "media_media_id_unique": { + "name": "media_media_id_unique", + "nullsNotDistinct": false, + "columns": [ + "media_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.media_settings": { + "name": "media_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apikey": { + "name": "apikey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "use_web_p": { + "name": "use_web_p", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "webp_output_quality": { + "name": "webp_output_quality", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "thumbnail_width": { + "name": "thumbnail_width", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "thumbnail_height": { + "name": "thumbnail_height", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "media_settings_user_id_user_id_fk": { + "name": "media_settings_user_id_user_id_fk", + "tableFrom": "media_settings", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "media_settings_apikey_unique": { + "name": "media_settings_apikey_unique", + "nullsNotDistinct": false, + "columns": [ + "apikey" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_app_selections": { + "name": "oauth_app_selections", + "schema": "", + "columns": { + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "api_key_id": { + "name": "api_key_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "oauth_app_selections_session_id_session_id_fk": { + "name": "oauth_app_selections_session_id_session_id_fk", + "tableFrom": "oauth_app_selections", + "tableTo": "session", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_app_selections_api_key_id_api_keys_id_fk": { + "name": "oauth_app_selections_api_key_id_api_keys_id_fk", + "tableFrom": "oauth_app_selections", + "tableTo": "api_keys", + "columnsFrom": [ + "api_key_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.profiles": { + "name": "profiles", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "public_user_id": { + "name": "public_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subscription_id": { + "name": "subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subscription_ends_after": { + "name": "subscription_ends_after", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "subscription_method": { + "name": "subscription_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subscription_status": { + "name": "subscription_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'not-subscribed'" + } + }, + "indexes": {}, + "foreignKeys": { + "profiles_user_id_user_id_fk": { + "name": "profiles_user_id_user_id_fk", + "tableFrom": "profiles", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "profiles_public_user_id_unique": { + "name": "profiles_public_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "public_user_id" + ] + }, + "profiles_customer_id_unique": { + "name": "profiles_customer_id_unique", + "nullsNotDistinct": false, + "columns": [ + "customer_id" + ] + }, + "profiles_subscription_id_unique": { + "name": "profiles_subscription_id_unique", + "nullsNotDistinct": false, + "columns": [ + "subscription_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.signatures": { + "name": "signatures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apikey": { + "name": "apikey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "signature": { + "name": "signature", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "valid_till": { + "name": "valid_till", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "signatures_signature_unique": { + "name": "signatures_signature_unique", + "nullsNotDistinct": false, + "columns": [ + "signature" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tus_uploads": { + "name": "tus_uploads", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "upload_id": { + "name": "upload_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "apikey": { + "name": "apikey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "upload_length": { + "name": "upload_length", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "upload_offset": { + "name": "upload_offset", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "temp_file_path": { + "name": "temp_file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "signature": { + "name": "signature", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_complete": { + "name": "is_complete", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "tus_uploads_upload_id_unique": { + "name": "tus_uploads_upload_id_unique", + "nullsNotDistinct": false, + "columns": [ + "upload_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.billing_catalog_revision_items": { + "name": "billing_catalog_revision_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "catalog_revision_id": { + "name": "catalog_revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "offer_key": { + "name": "offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_price_entry_id": { + "name": "billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "billing_catalog_revision_items_revision_key_uidx": { + "name": "billing_catalog_revision_items_revision_key_uidx", + "columns": [ + { + "expression": "catalog_revision_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "offer_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_catalog_revision_items_revision_price_uidx": { + "name": "billing_catalog_revision_items_revision_price_uidx", + "columns": [ + { + "expression": "catalog_revision_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "billing_price_entry_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_catalog_revision_items_catalog_revision_id_billing_catalog_revisions_id_fk": { + "name": "billing_catalog_revision_items_catalog_revision_id_billing_catalog_revisions_id_fk", + "tableFrom": "billing_catalog_revision_items", + "tableTo": "billing_catalog_revisions", + "columnsFrom": [ + "catalog_revision_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "billing_catalog_revision_items_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_catalog_revision_items_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_catalog_revision_items", + "tableTo": "billing_price_entries", + "columnsFrom": [ + "billing_price_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.billing_catalog_revisions": { + "name": "billing_catalog_revisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "checkout_provider": { + "name": "checkout_provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending_verification'" + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "activated_at": { + "name": "activated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "retired_at": { + "name": "retired_at", + "type": "timestamp with time zone", + "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": { + "billing_catalog_revisions_active_provider_uidx": { + "name": "billing_catalog_revisions_active_provider_uidx", + "columns": [ + { + "expression": "checkout_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_catalog_revisions\".\"status\" = 'active'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "billing_catalog_revisions_revision_unique": { + "name": "billing_catalog_revisions_revision_unique", + "nullsNotDistinct": false, + "columns": [ + "revision" + ] + } + }, + "policies": {}, + "checkConstraints": { + "billing_catalog_revisions_status_check": { + "name": "billing_catalog_revisions_status_check", + "value": "\"billing_catalog_revisions\".\"status\" IN ('pending_verification', 'active', 'retired', 'invalid', 'abandoned')" + }, + "billing_catalog_revisions_revision_check": { + "name": "billing_catalog_revisions_revision_check", + "value": "\"billing_catalog_revisions\".\"revision\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.billing_checkout_attempts": { + "name": "billing_checkout_attempts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "attempt_id": { + "name": "attempt_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billable_entity_id": { + "name": "billable_entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_id": { + "name": "payer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_email": { + "name": "payer_email", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "return_url": { + "name": "return_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "catalog_revision": { + "name": "catalog_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "offer_key": { + "name": "offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_plan": { + "name": "requested_plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_interval": { + "name": "requested_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_price_entry_id": { + "name": "billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "quoted_amount_minor": { + "name": "quoted_amount_minor", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "quoted_currency": { + "name": "quoted_currency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_customer_id": { + "name": "billing_customer_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider_checkout_session_id": { + "name": "provider_checkout_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "checkout_url_encrypted": { + "name": "checkout_url_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'creating'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "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": { + "billing_checkout_attempts_provider_session_uidx": { + "name": "billing_checkout_attempts_provider_session_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_checkout_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_checkout_attempts\".\"provider_checkout_session_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_checkout_attempts_idempotency_uidx": { + "name": "billing_checkout_attempts_idempotency_uidx", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_checkout_attempts_entity_nonterminal_uidx": { + "name": "billing_checkout_attempts_entity_nonterminal_uidx", + "columns": [ + { + "expression": "billable_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_checkout_attempts\".\"status\" IN ('creating', 'open')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_checkout_attempts_billable_entity_id_user_id_fk": { + "name": "billing_checkout_attempts_billable_entity_id_user_id_fk", + "tableFrom": "billing_checkout_attempts", + "tableTo": "user", + "columnsFrom": [ + "billable_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_checkout_attempts_payer_id_user_id_fk": { + "name": "billing_checkout_attempts_payer_id_user_id_fk", + "tableFrom": "billing_checkout_attempts", + "tableTo": "user", + "columnsFrom": [ + "payer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_checkout_attempts_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_checkout_attempts_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_checkout_attempts", + "tableTo": "billing_price_entries", + "columnsFrom": [ + "billing_price_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_checkout_attempts_billing_customer_id_billing_provider_customers_id_fk": { + "name": "billing_checkout_attempts_billing_customer_id_billing_provider_customers_id_fk", + "tableFrom": "billing_checkout_attempts", + "tableTo": "billing_provider_customers", + "columnsFrom": [ + "billing_customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "billing_checkout_attempts_attempt_id_unique": { + "name": "billing_checkout_attempts_attempt_id_unique", + "nullsNotDistinct": false, + "columns": [ + "attempt_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "billing_checkout_attempts_status_check": { + "name": "billing_checkout_attempts_status_check", + "value": "\"billing_checkout_attempts\".\"status\" IN ('creating', 'open', 'completed', 'expired', 'abandoned', 'conflicted')" + }, + "billing_checkout_attempts_amount_check": { + "name": "billing_checkout_attempts_amount_check", + "value": "\"billing_checkout_attempts\".\"quoted_amount_minor\" > 0" + }, + "billing_checkout_attempts_plan_check": { + "name": "billing_checkout_attempts_plan_check", + "value": "\"billing_checkout_attempts\".\"requested_plan\" IN ('pro')" + }, + "billing_checkout_attempts_interval_check": { + "name": "billing_checkout_attempts_interval_check", + "value": "\"billing_checkout_attempts\".\"requested_interval\" IN ('month', 'year')" + } + }, + "isRLSEnabled": false + }, + "public.billing_plan_change_attempts": { + "name": "billing_plan_change_attempts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "change_id": { + "name": "change_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billable_entity_id": { + "name": "billable_entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subscription_id": { + "name": "subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_id": { + "name": "payer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "current_catalog_revision": { + "name": "current_catalog_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "current_billing_price_entry_id": { + "name": "current_billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "current_plan": { + "name": "current_plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "current_interval": { + "name": "current_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_catalog_revision": { + "name": "target_catalog_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "target_billing_price_entry_id": { + "name": "target_billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "target_plan": { + "name": "target_plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_interval": { + "name": "target_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_offer_key": { + "name": "target_offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "effective_at": { + "name": "effective_at", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "proration_mode": { + "name": "proration_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_payment_id": { + "name": "provider_payment_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payment_url_encrypted": { + "name": "payment_url_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'creating'" + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "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": { + "billing_plan_change_attempts_idempotency_uidx": { + "name": "billing_plan_change_attempts_idempotency_uidx", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_plan_change_attempts_entity_nonterminal_uidx": { + "name": "billing_plan_change_attempts_entity_nonterminal_uidx", + "columns": [ + { + "expression": "billable_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_plan_change_attempts\".\"status\" IN ('creating', 'pending')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_plan_change_attempts_billable_entity_id_user_id_fk": { + "name": "billing_plan_change_attempts_billable_entity_id_user_id_fk", + "tableFrom": "billing_plan_change_attempts", + "tableTo": "user", + "columnsFrom": [ + "billable_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_plan_change_attempts_subscription_id_billing_subscriptions_id_fk": { + "name": "billing_plan_change_attempts_subscription_id_billing_subscriptions_id_fk", + "tableFrom": "billing_plan_change_attempts", + "tableTo": "billing_subscriptions", + "columnsFrom": [ + "subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_plan_change_attempts_payer_id_user_id_fk": { + "name": "billing_plan_change_attempts_payer_id_user_id_fk", + "tableFrom": "billing_plan_change_attempts", + "tableTo": "user", + "columnsFrom": [ + "payer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_plan_change_attempts_current_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_plan_change_attempts_current_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_plan_change_attempts", + "tableTo": "billing_price_entries", + "columnsFrom": [ + "current_billing_price_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_plan_change_attempts_target_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_plan_change_attempts_target_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_plan_change_attempts", + "tableTo": "billing_price_entries", + "columnsFrom": [ + "target_billing_price_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "billing_plan_change_attempts_change_id_unique": { + "name": "billing_plan_change_attempts_change_id_unique", + "nullsNotDistinct": false, + "columns": [ + "change_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "billing_plan_change_attempts_status_check": { + "name": "billing_plan_change_attempts_status_check", + "value": "\"billing_plan_change_attempts\".\"status\" IN ('creating', 'pending', 'succeeded', 'failed', 'conflicted')" + }, + "billing_plan_change_attempts_effective_at_check": { + "name": "billing_plan_change_attempts_effective_at_check", + "value": "\"billing_plan_change_attempts\".\"effective_at\" IN ('immediately', 'next_billing_date')" + }, + "billing_plan_change_attempts_proration_mode_check": { + "name": "billing_plan_change_attempts_proration_mode_check", + "value": "\"billing_plan_change_attempts\".\"proration_mode\" IN ('prorated_immediately', 'do_not_bill')" + }, + "billing_plan_change_attempts_current_plan_check": { + "name": "billing_plan_change_attempts_current_plan_check", + "value": "\"billing_plan_change_attempts\".\"current_plan\" IN ('pro')" + }, + "billing_plan_change_attempts_target_plan_check": { + "name": "billing_plan_change_attempts_target_plan_check", + "value": "\"billing_plan_change_attempts\".\"target_plan\" IN ('pro')" + } + }, + "isRLSEnabled": false + }, + "public.billing_plan_states": { + "name": "billing_plan_states", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "billable_entity_id": { + "name": "billable_entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active_subscription_id": { + "name": "active_subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "projection_version": { + "name": "projection_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "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": { + "billing_plan_states_billable_entity_id_user_id_fk": { + "name": "billing_plan_states_billable_entity_id_user_id_fk", + "tableFrom": "billing_plan_states", + "tableTo": "user", + "columnsFrom": [ + "billable_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_plan_states_active_subscription_id_billing_subscriptions_id_fk": { + "name": "billing_plan_states_active_subscription_id_billing_subscriptions_id_fk", + "tableFrom": "billing_plan_states", + "tableTo": "billing_subscriptions", + "columnsFrom": [ + "active_subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "billing_plan_states_billable_entity_id_unique": { + "name": "billing_plan_states_billable_entity_id_unique", + "nullsNotDistinct": false, + "columns": [ + "billable_entity_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.billing_price_entries": { + "name": "billing_price_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "offer_key": { + "name": "offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_interval": { + "name": "billing_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount_minor": { + "name": "amount_minor", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "provider_trial_days": { + "name": "provider_trial_days", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_product_id": { + "name": "provider_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp with time zone", + "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": { + "billing_price_entries_provider_product_uidx": { + "name": "billing_price_entries_provider_product_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_price_entries_offer_key_idx": { + "name": "billing_price_entries_offer_key_idx", + "columns": [ + { + "expression": "offer_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_price_entries_amount_check": { + "name": "billing_price_entries_amount_check", + "value": "\"billing_price_entries\".\"amount_minor\" > 0" + }, + "billing_price_entries_trial_days_check": { + "name": "billing_price_entries_trial_days_check", + "value": "\"billing_price_entries\".\"provider_trial_days\" >= 0" + }, + "billing_price_entries_currency_check": { + "name": "billing_price_entries_currency_check", + "value": "\"billing_price_entries\".\"currency\" ~ '^[A-Z]{3}$'" + }, + "billing_price_entries_plan_check": { + "name": "billing_price_entries_plan_check", + "value": "\"billing_price_entries\".\"plan\" IN ('pro')" + }, + "billing_price_entries_interval_check": { + "name": "billing_price_entries_interval_check", + "value": "\"billing_price_entries\".\"billing_interval\" IN ('month', 'year')" + } + }, + "isRLSEnabled": false + }, + "public.billing_provider_customers": { + "name": "billing_provider_customers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_id": { + "name": "payer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payer_email": { + "name": "payer_email", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "provider_customer_id": { + "name": "provider_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'creating'" + }, + "last_error": { + "name": "last_error", + "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": { + "billing_provider_customers_provider_payer_uidx": { + "name": "billing_provider_customers_provider_payer_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "payer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_provider_customers_provider_customer_uidx": { + "name": "billing_provider_customers_provider_customer_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_customer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_provider_customers\".\"provider_customer_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_provider_customers_idempotency_uidx": { + "name": "billing_provider_customers_idempotency_uidx", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_provider_customers_payer_id_user_id_fk": { + "name": "billing_provider_customers_payer_id_user_id_fk", + "tableFrom": "billing_provider_customers", + "tableTo": "user", + "columnsFrom": [ + "payer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_provider_customers_status_check": { + "name": "billing_provider_customers_status_check", + "value": "\"billing_provider_customers\".\"status\" IN ('creating', 'active', 'conflicted')" + } + }, + "isRLSEnabled": false + }, + "public.billing_reconciliation_jobs": { + "name": "billing_reconciliation_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "checkout_attempt_id": { + "name": "checkout_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "plan_change_attempt_id": { + "name": "plan_change_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "subscription_id": { + "name": "subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider_customer_id": { + "name": "provider_customer_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "operation": { + "name": "operation", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'reconcile'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "available_at": { + "name": "available_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "locked_at": { + "name": "locked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "worker_id": { + "name": "worker_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "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": { + "billing_reconciliation_jobs_live_checkout_uidx": { + "name": "billing_reconciliation_jobs_live_checkout_uidx", + "columns": [ + { + "expression": "checkout_attempt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_reconciliation_jobs\".\"checkout_attempt_id\" IS NOT NULL AND \"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_reconciliation_jobs_live_plan_change_uidx": { + "name": "billing_reconciliation_jobs_live_plan_change_uidx", + "columns": [ + { + "expression": "plan_change_attempt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_reconciliation_jobs\".\"plan_change_attempt_id\" IS NOT NULL AND \"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_reconciliation_jobs_live_subscription_uidx": { + "name": "billing_reconciliation_jobs_live_subscription_uidx", + "columns": [ + { + "expression": "subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_reconciliation_jobs\".\"subscription_id\" IS NOT NULL AND \"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_reconciliation_jobs_live_customer_uidx": { + "name": "billing_reconciliation_jobs_live_customer_uidx", + "columns": [ + { + "expression": "provider_customer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_reconciliation_jobs\".\"provider_customer_id\" IS NOT NULL AND \"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_reconciliation_jobs_checkout_attempt_id_billing_checkout_attempts_id_fk": { + "name": "billing_reconciliation_jobs_checkout_attempt_id_billing_checkout_attempts_id_fk", + "tableFrom": "billing_reconciliation_jobs", + "tableTo": "billing_checkout_attempts", + "columnsFrom": [ + "checkout_attempt_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_reconciliation_jobs_plan_change_attempt_id_billing_plan_change_attempts_id_fk": { + "name": "billing_reconciliation_jobs_plan_change_attempt_id_billing_plan_change_attempts_id_fk", + "tableFrom": "billing_reconciliation_jobs", + "tableTo": "billing_plan_change_attempts", + "columnsFrom": [ + "plan_change_attempt_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_reconciliation_jobs_subscription_id_billing_subscriptions_id_fk": { + "name": "billing_reconciliation_jobs_subscription_id_billing_subscriptions_id_fk", + "tableFrom": "billing_reconciliation_jobs", + "tableTo": "billing_subscriptions", + "columnsFrom": [ + "subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_reconciliation_jobs_provider_customer_id_billing_provider_customers_id_fk": { + "name": "billing_reconciliation_jobs_provider_customer_id_billing_provider_customers_id_fk", + "tableFrom": "billing_reconciliation_jobs", + "tableTo": "billing_provider_customers", + "columnsFrom": [ + "provider_customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_reconciliation_jobs_exactly_one_subject": { + "name": "billing_reconciliation_jobs_exactly_one_subject", + "value": "((CASE WHEN \"billing_reconciliation_jobs\".\"checkout_attempt_id\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"billing_reconciliation_jobs\".\"plan_change_attempt_id\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"billing_reconciliation_jobs\".\"subscription_id\" IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN \"billing_reconciliation_jobs\".\"provider_customer_id\" IS NOT NULL THEN 1 ELSE 0 END)) = 1" + }, + "billing_reconciliation_jobs_status_check": { + "name": "billing_reconciliation_jobs_status_check", + "value": "\"billing_reconciliation_jobs\".\"status\" IN ('pending', 'processing', 'failed', 'completed', 'quarantined')" + }, + "billing_reconciliation_jobs_operation_check": { + "name": "billing_reconciliation_jobs_operation_check", + "value": "\"billing_reconciliation_jobs\".\"operation\" = 'reconcile' OR (\"billing_reconciliation_jobs\".\"operation\" = 'cancellation' AND \"billing_reconciliation_jobs\".\"subscription_id\" IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.billing_subscriptions": { + "name": "billing_subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "billable_entity_id": { + "name": "billable_entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_customer_id": { + "name": "billing_customer_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "payer_id": { + "name": "payer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "origin_checkout_attempt_id": { + "name": "origin_checkout_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_subscription_id": { + "name": "provider_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_product_id": { + "name": "provider_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_price_entry_id": { + "name": "billing_price_entry_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "catalog_revision": { + "name": "catalog_revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "offer_key": { + "name": "offer_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "billing_interval": { + "name": "billing_interval", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "current_period_starts_at": { + "name": "current_period_starts_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "current_period_ends_at": { + "name": "current_period_ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "paid_through_at": { + "name": "paid_through_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "trial_ends_at": { + "name": "trial_ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cancel_at_period_end": { + "name": "cancel_at_period_end", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_entitlement_source": { + "name": "is_entitlement_source", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "provider_occurred_at": { + "name": "provider_occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "provider_version": { + "name": "provider_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_observed_at": { + "name": "last_observed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_reconciled_at": { + "name": "last_reconciled_at", + "type": "timestamp with time zone", + "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": { + "billing_subscriptions_provider_subscription_uidx": { + "name": "billing_subscriptions_provider_subscription_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_subscriptions_entity_source_uidx": { + "name": "billing_subscriptions_entity_source_uidx", + "columns": [ + { + "expression": "billable_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"billing_subscriptions\".\"is_entitlement_source\" = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "billing_subscriptions_billable_entity_id_user_id_fk": { + "name": "billing_subscriptions_billable_entity_id_user_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "user", + "columnsFrom": [ + "billable_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_subscriptions_billing_customer_id_billing_provider_customers_id_fk": { + "name": "billing_subscriptions_billing_customer_id_billing_provider_customers_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "billing_provider_customers", + "columnsFrom": [ + "billing_customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_subscriptions_payer_id_user_id_fk": { + "name": "billing_subscriptions_payer_id_user_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "user", + "columnsFrom": [ + "payer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_subscriptions_origin_checkout_attempt_id_billing_checkout_attempts_id_fk": { + "name": "billing_subscriptions_origin_checkout_attempt_id_billing_checkout_attempts_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "billing_checkout_attempts", + "columnsFrom": [ + "origin_checkout_attempt_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "billing_subscriptions_billing_price_entry_id_billing_price_entries_id_fk": { + "name": "billing_subscriptions_billing_price_entry_id_billing_price_entries_id_fk", + "tableFrom": "billing_subscriptions", + "tableTo": "billing_price_entries", + "columnsFrom": [ + "billing_price_entry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_subscriptions_status_check": { + "name": "billing_subscriptions_status_check", + "value": "\"billing_subscriptions\".\"status\" IN ('pending', 'trialing', 'active', 'past_due', 'cancelled', 'expired')" + }, + "billing_subscriptions_plan_check": { + "name": "billing_subscriptions_plan_check", + "value": "\"billing_subscriptions\".\"plan\" IN ('pro')" + }, + "billing_subscriptions_interval_check": { + "name": "billing_subscriptions_interval_check", + "value": "\"billing_subscriptions\".\"billing_interval\" IN ('month', 'year')" + } + }, + "isRLSEnabled": false + }, + "public.billing_webhook_events": { + "name": "billing_webhook_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_event_id": { + "name": "provider_event_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "subscription_id": { + "name": "subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "checkout_attempt_id": { + "name": "checkout_attempt_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload_encrypted": { + "name": "payload_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload_key_version": { + "name": "payload_key_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "verified_key_version": { + "name": "verified_key_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "processing_attempts": { + "name": "processing_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "available_at": { + "name": "available_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "locked_at": { + "name": "locked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "worker_id": { + "name": "worker_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "received_at": { + "name": "received_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "billing_webhook_events_provider_event_uidx": { + "name": "billing_webhook_events_provider_event_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_webhook_events_queue_idx": { + "name": "billing_webhook_events_queue_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "available_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "billing_webhook_events_status_check": { + "name": "billing_webhook_events_status_check", + "value": "\"billing_webhook_events\".\"status\" IN ('pending', 'processing', 'processed', 'ignored', 'quarantined', 'failed')" + } + }, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/api/drizzle/meta/_journal.json b/apps/api/drizzle/meta/_journal.json new file mode 100644 index 00000000..3a639b5c --- /dev/null +++ b/apps/api/drizzle/meta/_journal.json @@ -0,0 +1,55 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1710000000000, + "tag": "0000_auth", + "breakpoints": true + }, + { + "idx": 1, + "version": "7", + "when": 1710000001000, + "tag": "0001_domain", + "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1710000002000, + "tag": "0002_billing", + "breakpoints": true + }, + { + "idx": 3, + "version": "7", + "when": 1790985600000, + "tag": "0003_auth_account_issuer_nullable", + "breakpoints": true + }, + { + "idx": 4, + "version": "7", + "when": 1791052825193, + "tag": "0004_drop_legacy_revoked_tokens", + "breakpoints": true + }, + { + "idx": 5, + "version": "7", + "when": 1791057896153, + "tag": "0005_drop_account_issuer", + "breakpoints": true + }, + { + "idx": 6, + "version": "7", + "when": 1791128530445, + "tag": "0006_oauth_app_selections", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/apps/api/package.json b/apps/api/package.json index b405a4d3..a256537f 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -1,83 +1,86 @@ { - "name": "@medialit/api", - "version": "0.3.1", - "private": true, - "description": "A better API to manage your files on AWS S3 compatible cloud storages", - "author": { - "name": "The CodeLit Team", - "email": "hi@codelit.dev" - }, - "homepage": "https://github.com/codelitdev/medialit#readme", - "license": "AGPL-3.0-or-later", - "main": "dist/src/index.js", - "directories": { - "lib": "lib", - "test": "__tests__" - }, - "files": [ - "dist" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/codelitdev/medialit.git" - }, - "bugs": { - "url": "https://github.com/codelitdev/medialit/issues" - }, - "scripts": { - "build": "pnpm run swagger:generate && tsc", - "swagger:generate": "node --import tsx src/swagger-generator.ts", - "dev": "pnpm run swagger:generate && nodemon --exec 'node --env-file=.env --import tsx' src/index.ts", - "start": "node dist/src/index.js", - "test": "node --import tsx --test '**/*.test.ts'", - "test:fuzz": "uvx schemathesis run http://127.0.0.1:8000/openapi.json" - }, - "dependencies": { - "@aws-sdk/client-s3": "^3.922.0", - "@aws-sdk/cloudfront-signer": "^3.572.0", - "@aws-sdk/s3-request-presigner": "^3.922.0", - "@medialit/images": "workspace:*", - "@medialit/models": "workspace:*", - "@medialit/thumbnail": "workspace:*", - "@medialit/utils": "workspace:^0.1.0", - "@modelcontextprotocol/sdk": "^1.29.0", - "@node-oauth/oauth2-server": "^5.3.0", - "@tus/file-store": "^2.0.0", - "@tus/server": "^2.3.0", - "aws-sdk": "^2.1692.0", - "cors": "^2.8.5", - "dotenv": "^17.2.3", - "express": "^4.2.0", - "express-fileupload": "^1.5.2", - "express-rate-limit": "^7.5.0", - "joi": "^17.6.0", - "joi-to-swagger": "^6.2.0", - "jsonwebtoken": "^9.0.2", - "mongoose": "^8.19.3", - "passport": "^0.7.0", - "passport-jwt": "^4.0.1", - "pino": "^10.1.0", - "swagger-ui-express": "^5.0.1", - "nodemailer": "^6.10.0", - "zod": "^3.25.76" - }, - "devDependencies": { - "@types/cors": "^2.8.12", - "@types/express": "^4.17.20", - "@types/express-fileupload": "^1.2.2", - "@types/joi": "^17.2.3", - "@types/jsonwebtoken": "^9.0.10", - "@types/node": "^22.14.1", - "@types/nodemailer": "^6.4.17", - "@types/passport": "^1.0.7", - "@types/passport-jwt": "^3.0.6", - "@types/swagger-ui-express": "^4.1.8", - "@typescript-eslint/eslint-plugin": "^5.17.0", - "@typescript-eslint/parser": "^5.17.0", - "eslint": "^8.12.0", - "nodemon": "^3.1.10", - "swagger-autogen": "^2.23.7", - "tsx": "^4.20.6", - "typescript": "^5.9.3" - } -} \ No newline at end of file + "name": "@medialit/api", + "version": "0.3.1", + "private": true, + "description": "A better API to manage your files on AWS S3 compatible cloud storages", + "author": { + "name": "The CodeLit Team", + "email": "hi@codelit.dev" + }, + "homepage": "https://github.com/codelitdev/medialit#readme", + "license": "AGPL-3.0-or-later", + "main": "src/index.ts", + "repository": { + "type": "git", + "url": "git+https://github.com/codelitdev/medialit.git" + }, + "bugs": { + "url": "https://github.com/codelitdev/medialit/issues" + }, + "scripts": { + "build": "tsc -p tsconfig.json --noEmit", + "typecheck": "tsc -p tsconfig.json --noEmit", + "swagger:generate": "bun src/swagger-generator.ts", + "dev": "bun --watch --env-file=.env src/index.ts", + "start": "bun src/index.ts", + "test": "node --require ./test-preload.cjs --import tsx --test '**/*.test.ts'", + "test:conformance": "node --require ./test-preload.cjs --import tsx --test --test-name-pattern \"platform conformance\" '**/*.test.ts'", + "billing:generate": "codelit-billing generate --config billing.config.ts", + "billing:generate:check": "codelit-billing generate --check --config billing.config.ts", + "check:drift": "bun run billing:generate:check", + "db:generate": "drizzle-kit generate", + "db:migrate": "bun --env-file=.env src/db/migrate.ts", + "db:push": "drizzle-kit push", + "db:studio": "drizzle-kit studio" + }, + "dependencies": { + "@aws-sdk/client-s3": "^3.1146.0", + "@aws-sdk/cloudfront-signer": "^3.1138.0", + "@aws-sdk/s3-request-presigner": "^3.1146.0", + "@better-auth/cimd": "1.7.7", + "@better-auth/oauth-provider": "1.7.7", + "@codelitdev/billing": "0.3.0", + "@codelitdev/mcp-server-kit": "0.3.0", + "@codelitdev/oauth-server-kit": "0.3.0", + "@codelitdev/observability": "0.3.0", + "@codelitdev/platform": "0.3.0", + "@medialit/images": "workspace:*", + "@medialit/models": "workspace:*", + "@medialit/thumbnail": "workspace:*", + "@medialit/utils": "workspace:*", + "@modelcontextprotocol/sdk": "1.30.0", + "@tus/file-store": "^2.1.1", + "@tus/server": "^2.4.5", + "better-auth": "1.7.7", + "cors": "^2.8.5", + "dodopayments": "2.48.0", + "dotenv": "^17.2.3", + "drizzle-orm": "0.45.2", + "express": "4.21.2", + "express-fileupload": "^1.5.2", + "express-rate-limit": "^7.5.0", + "joi": "^17.13.7", + "joi-to-swagger": "^6.2.0", + "jsonwebtoken": "^9.0.2", + "nodemailer": "^6.10.0", + "pg": "8.23.0", + "swagger-ui-express": "^5.0.1", + "zod": "3.25.76" + }, + "devDependencies": { + "@codelitdev/platform-conformance": "0.3.0", + "@electric-sql/pglite": "0.5.8", + "@types/cors": "^2.8.12", + "@types/express": "^4.17.20", + "@types/express-fileupload": "^1.2.2", + "@types/jsonwebtoken": "^9.0.10", + "@types/node": "^22.14.1", + "@types/nodemailer": "^6.4.17", + "@types/pg": "8.23.1", + "@types/swagger-ui-express": "^4.1.8", + "drizzle-kit": "0.31.10", + "swagger-autogen": "^2.23.7", + "tsx": "^4.20.6", + "typescript": "5.9.3" + } +} diff --git a/apps/api/src/__tests__/lifecycle.test.ts b/apps/api/src/__tests__/lifecycle.test.ts new file mode 100644 index 00000000..8475ea94 --- /dev/null +++ b/apps/api/src/__tests__/lifecycle.test.ts @@ -0,0 +1,103 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { apiReadiness, createApiShutdown } from "../lifecycle.js"; + +function recordingSteps(calls: string[]) { + return { + stopBackgroundJobs: () => { + calls.push("jobs"); + }, + closeMcpSessions: async () => { + calls.push("mcp"); + }, + closeServer: async () => { + await new Promise((resolve) => setTimeout(resolve, 5)); + calls.push("server"); + }, + closeDatabase: async () => { + calls.push("database"); + }, + flushObservability: async () => { + calls.push("observability"); + }, + }; +} + +test("shutdown closes the database only after the server drains", async () => { + const calls: string[] = []; + const shutdown = createApiShutdown(recordingSteps(calls)); + assert.equal(shutdown.shuttingDown(), false); + await shutdown.shutdown(); + assert.equal(shutdown.shuttingDown(), true); + assert.deepEqual(calls, [ + "jobs", + "mcp", + "server", + "database", + "observability", + ]); +}); + +test("repeated signals run shutdown once", async () => { + const calls: string[] = []; + const shutdown = createApiShutdown(recordingSteps(calls)); + await Promise.all([shutdown.shutdown(), shutdown.shutdown()]); + assert.equal(calls.filter((call) => call === "server").length, 1); +}); + +test("shutdown rejects when a step hangs past the timeout", async () => { + const steps = { + ...recordingSteps([]), + closeServer: () => new Promise(() => {}), + }; + await assert.rejects( + createApiShutdown(steps, 20).shutdown(), + /shutdown_timeout/, + ); +}); + +test("readiness requires startup, no shutdown, and a reachable database", async () => { + const ping = async () => undefined; + assert.equal( + ( + await apiReadiness({ + started: true, + shuttingDown: false, + pingDatabase: ping, + }) + ).status, + "ready", + ); + + let pinged = false; + const notStarted = await apiReadiness({ + started: false, + shuttingDown: false, + pingDatabase: async () => { + pinged = true; + }, + }); + assert.equal(notStarted.status, "not_ready"); + assert.equal(pinged, false); + + const draining = await apiReadiness({ + started: true, + shuttingDown: true, + pingDatabase: ping, + }); + assert.deepEqual(draining.checks, { + started: true, + accepting_requests: false, + database: false, + }); + + const databaseDown = await apiReadiness({ + started: true, + shuttingDown: false, + pingDatabase: async () => { + throw new Error("connection refused"); + }, + }); + assert.equal(databaseDown.status, "not_ready"); + assert.equal(databaseDown.checks.database, false); +}); diff --git a/apps/api/src/__tests__/platform-conformance.test.ts b/apps/api/src/__tests__/platform-conformance.test.ts new file mode 100644 index 00000000..67fe52da --- /dev/null +++ b/apps/api/src/__tests__/platform-conformance.test.ts @@ -0,0 +1,100 @@ +import { after, before, test } from "node:test"; +import assert from "node:assert/strict"; +import { createServer, type Server } from "node:http"; +import type { AddressInfo } from "node:net"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { PGlite } from "@electric-sql/pglite"; +import { drizzle } from "drizzle-orm/pglite"; +import { migrate } from "drizzle-orm/pglite/migrator"; +import type { AppDb } from "../db/client.js"; +import { + type McpDiscoveryAdapter, + runMcpDiscoveryConformance, +} from "@codelitdev/platform-conformance"; + +/* + * MediaLit declares the auth and MCP capabilities. Its domain is media, not + * the reference product's notes, so `runPlatformConformance` does not apply. + * Billing, observability, and lifecycle stay covered by their own tests. + */ +let server: Server; +let origin: string; +let adapter: McpDiscoveryAdapter; +let client: PGlite; + +before(async () => { + // Better Auth's OAuth provider seeds its resources on first use, so the + // real app needs a migrated database even for anonymous discovery. + const schema = await import("../db/schema/index.js"); + const { setDb } = await import("../db/client.js"); + client = new PGlite(); + const db = drizzle(client, { schema }); + await migrate(db, { + migrationsFolder: path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + "../../drizzle", + ), + }); + setDb(db as unknown as AppDb); + + const { createMedialitAuth } = await import("../auth/better-auth.js"); + const { createApp } = await import("../app.js"); + const { apiReadiness } = await import("../lifecycle.js"); + + server = createServer(); + await new Promise((resolve) => + server.listen(0, "127.0.0.1", resolve), + ); + origin = `http://127.0.0.1:${(server.address() as AddressInfo).port}`; + + const auth = createMedialitAuth({ + publicApiUrl: origin, + secret: "conformance-secret-that-is-at-least-32-chars", + webOrigin: "http://127.0.0.1:3000", + }); + const app = createApp({ + auth, + webOrigin: "http://127.0.0.1:3000", + readiness: () => + apiReadiness({ + started: false, + shuttingDown: false, + pingDatabase: async () => undefined, + }), + }); + server.on("request", app); + + adapter = { + async request(input) { + const response = await fetch(`${origin}${input.path}`, { + method: input.method, + headers: input.headers, + ...(input.body === undefined + ? {} + : { body: JSON.stringify(input.body) }), + }); + const text = await response.text(); + return { + status: response.status, + headers: Object.fromEntries(response.headers.entries()), + body: text ? JSON.parse(text) : null, + }; + }, + }; +}); + +after(async () => { + server.closeAllConnections(); + await new Promise((resolve) => server.close(() => resolve())); + const { closeDb } = await import("../db/client.js"); + await closeDb(); + await client.close(); +}); + +test("platform conformance: MCP OAuth discovery", async () => { + const result = await runMcpDiscoveryConformance(adapter, { + resourceUrl: `${origin}/mcp`, + }); + assert.deepEqual(result.failures, []); +}); diff --git a/apps/api/src/apikey/model.ts b/apps/api/src/apikey/model.ts deleted file mode 100644 index 4ca44e74..00000000 --- a/apps/api/src/apikey/model.ts +++ /dev/null @@ -1,4 +0,0 @@ -import mongoose from "mongoose"; -import { ApikeySchema } from "@medialit/models"; - -export default mongoose.models.Apikey || mongoose.model("Apikey", ApikeySchema); diff --git a/apps/api/src/apikey/queries.ts b/apps/api/src/apikey/queries.ts index b3bca1f0..55d58f41 100644 --- a/apps/api/src/apikey/queries.ts +++ b/apps/api/src/apikey/queries.ts @@ -1,52 +1,30 @@ import { Apikey } from "@medialit/models"; -import ApikeyModel from "./model"; -import { getUniqueId } from "@medialit/utils"; +import { + createApiKey as insertApiKey, + getApiKeyBySecret, + listApiKeys, +} from "@/db"; export async function createApiKey( userId: string, name: string, isDefault: boolean = false, ): Promise { - return await ApikeyModel.create({ - name, - key: getUniqueId(), - userId, - default: isDefault, - }); + const created = await insertApiKey({ userId, name, isDefault }); + return created; } export async function getApiKeyUsingKeyId(key: string): Promise { - return await ApikeyModel.findOne({ key }); + return getApiKeyBySecret(key); } export async function getApiKeyByUserId( userId: string, keyId?: string, ): Promise { - let result: Apikey | Apikey[] | null; - const projections = { - _id: 0, - name: 1, - key: 1, - httpReferrers: 1, - ipAddresses: 1, - default: 1, - createdAt: 1, - updatedAt: 1, - }; - if (keyId) { - result = await ApikeyModel.findOne( - { - key: keyId, - userId, - }, - projections, - ); - } else { - result = await ApikeyModel.find({ userId }, projections); - } - - return result; + const rows = await listApiKeys(userId, keyId); + if (keyId) return rows[0] ?? null; + return rows; } export default { diff --git a/apps/api/src/app.ts b/apps/api/src/app.ts new file mode 100644 index 00000000..177cba73 --- /dev/null +++ b/apps/api/src/app.ts @@ -0,0 +1,215 @@ +import express from "express"; +import rateLimit from "express-rate-limit"; +import mediaRoutes from "./media/routes"; +import signatureRoutes from "./signature/routes"; +import mediaSettingsRoutes from "./media-settings/routes"; +import tusRoutes from "./tus/routes"; +import mcpRoutes from "./mcp/routes"; +import { captureException } from "./services/log"; +import swaggerUi from "swagger-ui-express"; +import swaggerOutput from "./swagger_output.json"; +import { + createMcpOAuthDiscoveryRoutes, + type BetterAuthMetadataApi, +} from "@codelitdev/oauth-server-kit/mcp"; +import { createOAuthPagesRouter } from "@codelitdev/oauth-server-kit/express"; +import { toNodeHandler } from "better-auth/node"; +import type { apiReadiness } from "./lifecycle"; +import { cleanupTUSUploads } from "./tus/cleanup"; +import { cleanupExpiredTempUploads } from "./media/cleanup"; +import type { MedialitAuth } from "./auth/better-auth"; +import { AUTH_BASE_PATH, MCP_SCOPES_SUPPORTED } from "./auth/options"; +import { setBearerAuth } from "./auth/bearer"; +import { createOAuthAppPages } from "./auth/oauth-app-pages"; +import { oauthAppSelectionAdapter } from "./auth/oauth-app-selection"; +import { createDashboardRouter } from "./dashboard/routes"; +import { createBillingRouter, dodoWebhookRouter } from "./billing/routes"; + +type ReadinessReport = Awaited>; + +/** + * Compose the API's HTTP stack. `index.ts` serves it in production and the + * platform conformance test serves the same app on an ephemeral port. + */ +export function createApp(input: { + auth: MedialitAuth; + webOrigin: string; + readiness: () => Promise; +}): express.Express { + const { auth, webOrigin } = input; + const app = express(); + + app.set( + "trust proxy", + process.env.ENABLE_TRUST_PROXY === "true" ? 1 : false, + ); + + setBearerAuth(auth); + app.use( + createMcpOAuthDiscoveryRoutes({ + auth: auth.auth as unknown as BetterAuthMetadataApi, + oauthResourceClient: auth.oauthResourceClient, + resourceUrl: auth.mcpResource, + scopesSupported: [...MCP_SCOPES_SUPPORTED], + allowedOrigins: "*", + }), + ); + app.use( + `${AUTH_BASE_PATH}/oauth2/register`, + rateLimit({ + windowMs: 60_000, + max: 20, + standardHeaders: true, + legacyHeaders: false, + message: { + error: "too_many_requests", + error_description: "Too many client registration requests.", + }, + }), + ); + app.all(`${AUTH_BASE_PATH}/*`, toNodeHandler(auth.auth)); + app.use( + createOAuthPagesRouter({ + appName: "MediaLit", + authBasePath: AUTH_BASE_PATH, + allowedRedirectOrigins: [new URL(webOrigin).origin], + defaultRedirectUrl: `${webOrigin}/`, + loginMethods: [{ type: "email-otp" }], + }), + ); + app.use( + createOAuthAppPages({ + auth: auth.auth, + adapter: oauthAppSelectionAdapter, + authBasePath: AUTH_BASE_PATH, + }), + ); + + app.use( + express.json({ + limit: "2mb", + verify: (req, _res, buf) => { + (req as express.Request & { rawBody?: Buffer }).rawBody = + Buffer.from(buf); + }, + }), + ); + app.use(express.urlencoded({ extended: false })); + + app.get( + "/health", + /* + #swagger.summary = 'Status of the server', + #swagger.description = 'Returns the status of the server and uptime' + #swagger.responses[200] = { + description: "OK", + content: { + "application/json": { + schema: { + type: "object", + properties: { + status: { + type: "string", + example: "ok", + }, + uptime: { + type: "number", + example: 12.345, + }, + }, + }, + }, + }, + } + */ + (req, res) => { + res.status(200).json({ + status: "ok", + uptime: process.uptime(), + }); + }, + ); + + app.get( + "/ready", + /* #swagger.ignore = true */ + async (req, res) => { + const report = await input.readiness(); + res.status(report.status === "ready" ? 200 : 503).json(report); + }, + ); + + app.get( + "/openapi.json", + /* #swagger.ignore = true */ + (req, res) => { + res.json(swaggerOutput); + }, + ); + + app.use( + "/docs", + swaggerUi.serve, + swaggerUi.setup(swaggerOutput, { + explorer: true, + swaggerOptions: { + persistAuthorization: true, + displayRequestDuration: true, + docExpansion: "none", + defaultModelsExpandDepth: -1, + validatorUrl: null, + }, + }), + ); + + app.use("/settings/media", mediaSettingsRoutes(undefined)); + app.use("/media/signature", signatureRoutes); + app.use("/media", tusRoutes); + app.use("/media", mediaRoutes); + app.use(mcpRoutes); + + app.get( + "/cleanup/temp", + /* #swagger.ignore = true */ + async (req, res) => { + await cleanupExpiredTempUploads(); + res.status(200).json({ + message: "Expired temp uploads cleaned up", + }); + }, + ); + app.get( + "/cleanup/tus", + /* #swagger.ignore = true */ + async (req, res) => { + await cleanupTUSUploads(); + res.status(200).json({ + message: "Expired tus uploads cleaned up", + }); + }, + ); + + app.use(createDashboardRouter(auth)); + app.use(createBillingRouter(auth)); + app.use("/payment/webhook/dodo", dodoWebhookRouter()); + app.use(jsonErrorHandler); + return app; +} + +function jsonErrorHandler( + error: unknown, + req: express.Request, + res: express.Response, + next: express.NextFunction, +) { + captureException({ + error, + source: "express", + context: { method: req.method, path: req.path }, + }); + if (res.headersSent) { + next(error); + return; + } + res.status(500).json({ error: "Internal Server Error" }); +} diff --git a/apps/api/src/auth/__tests__/middleware.test.ts b/apps/api/src/auth/__tests__/middleware.test.ts index e313b3e0..c7ad6c16 100644 --- a/apps/api/src/auth/__tests__/middleware.test.ts +++ b/apps/api/src/auth/__tests__/middleware.test.ts @@ -109,7 +109,7 @@ test("MCP middleware maps OAuth auth fields", async () => { assert.equal(req.apikey, "default-key"); }); -test("MCP middleware maps API-key auth fields without OAuth fields", async () => { +test("MCP middleware maps API-key auth fields with full data scopes", async () => { const req: any = { headers: { "x-medialit-apikey": "submitted-key" }, body: {}, @@ -133,7 +133,8 @@ test("MCP middleware maps API-key auth fields without OAuth fields", async () => assert.equal(req.userId, "user-1"); assert.equal(req.apikey, "submitted-key"); assert.equal(req.clientId, undefined); - assert.equal(req.scopes, undefined); + // API keys predate scopes and keep full access. + assert.deepEqual(req.scopes, ["data:read", "data:write"]); }); test("MCP middleware preserves missing-auth 401 behavior", async () => { diff --git a/apps/api/src/auth/__tests__/oauth-app-selection.test.ts b/apps/api/src/auth/__tests__/oauth-app-selection.test.ts new file mode 100644 index 00000000..9d253e05 --- /dev/null +++ b/apps/api/src/auth/__tests__/oauth-app-selection.test.ts @@ -0,0 +1,82 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { + createOAuthAppSelectionHooks, + type OAuthApp, + type OAuthAppSelectionAdapter, +} from "../oauth-app-selection.js"; + +const NOW = Date.parse("2026-10-04T12:00:00Z"); +const input = { user: { id: "user-1" }, session: { id: "session-1" } }; + +function app(keyId: string, isDefault = false): OAuthApp { + return { id: `id-${keyId}`, keyId, name: keyId, isDefault }; +} + +function hooks( + apps: OAuthApp[], + selection: { keyId: string; updatedAt: Date } | null = null, +) { + const adapter: OAuthAppSelectionAdapter = { + listAppsForUser: async () => apps, + getSelection: async () => selection, + setSelection: async () => {}, + }; + return createOAuthAppSelectionHooks({ + page: "https://api.example/oauth/select-app", + adapter, + now: () => NOW, + }); +} + +test("a user with one app skips the picker and gets that app", async () => { + const { postLogin } = hooks([app("only", true)]); + + assert.equal(await postLogin.shouldRedirect(input), false); + assert.equal(await postLogin.consentReferenceId(input), "only"); +}); + +test("a user with several apps is sent to the picker", async () => { + const { postLogin } = hooks([app("a", true), app("b")]); + + assert.equal(await postLogin.shouldRedirect(input), true); + assert.equal(await postLogin.consentReferenceId(input), undefined); +}); + +test("a fresh selection continues the flow with the picked app", async () => { + const { postLogin } = hooks([app("a", true), app("b")], { + keyId: "b", + updatedAt: new Date(NOW - 60_000), + }); + + assert.equal(await postLogin.shouldRedirect(input), false); + assert.equal(await postLogin.consentReferenceId(input), "b"); +}); + +test("a selection from an earlier authorization shows the picker again", async () => { + const { postLogin } = hooks([app("a", true), app("b")], { + keyId: "b", + updatedAt: new Date(NOW - 60 * 60_000), + }); + + assert.equal(await postLogin.shouldRedirect(input), true); +}); + +test("a selection of an app the user no longer has is ignored", async () => { + const { postLogin } = hooks([app("a", true), app("b")], { + keyId: "gone", + updatedAt: new Date(NOW), + }); + + assert.equal(await postLogin.shouldRedirect(input), true); + assert.equal(await postLogin.consentReferenceId(input), undefined); +}); + +test("the picked app becomes the token's app_id claim", async () => { + const { customAccessTokenClaims } = hooks([]); + + assert.deepEqual(await customAccessTokenClaims({ referenceId: "b" }), { + app_id: "b", + }); + assert.deepEqual(await customAccessTokenClaims({}), {}); +}); diff --git a/apps/api/src/auth/__tests__/resolve-auth.test.ts b/apps/api/src/auth/__tests__/resolve-auth.test.ts index c60f04ae..c1bfb8ff 100644 --- a/apps/api/src/auth/__tests__/resolve-auth.test.ts +++ b/apps/api/src/auth/__tests__/resolve-auth.test.ts @@ -1,6 +1,10 @@ import { test } from "node:test"; import assert from "node:assert/strict"; -import { resolveAuth, selectEffectiveApiKey } from "../resolve-auth.js"; +import { + resolveAuth, + selectEffectiveApiKey, + sendAuthError, +} from "../resolve-auth.js"; import { Apikey } from "@medialit/models"; const user = { _id: "user-1", id: "user-1" }; @@ -75,6 +79,50 @@ test("OAuth auth falls back to the first API key when no default exists", async assert.equal(auth.apiKey, "first"); }); +test("OAuth auth uses the app picked at authorization", async () => { + const lookups: unknown[] = []; + const auth = await resolveAuth( + { authorization: "Bearer valid-token" }, + dependencies({ + validateBearerToken: async () => ({ + userId: "user-1", + clientId: "client-1", + scopes: ["read"], + appId: "picked", + }), + getApiKeyByUserId: async (userId: string, keyId?: string) => { + lookups.push([userId, keyId]); + return keyId === "picked" + ? apiKey("picked-secret", { keyId: "picked" }) + : [apiKey("default", { default: true })]; + }, + }), + ); + + assert.deepEqual(lookups, [["user-1", "picked"]]); + assert.equal(auth.status, "authenticated"); + assert.equal(auth.kind, "oauth"); + assert.equal(auth.apiKey, "picked-secret"); +}); + +test("OAuth auth rejects a token whose picked app was deleted", async () => { + const auth = await resolveAuth( + { authorization: "Bearer valid-token" }, + dependencies({ + validateBearerToken: async () => ({ + userId: "user-1", + clientId: "client-1", + scopes: ["read"], + appId: "deleted", + }), + getApiKeyByUserId: async (_userId: string, keyId?: string) => + keyId ? null : [apiKey("default", { default: true })], + }), + ); + + assert.equal(auth.status, "invalid_token"); +}); + test("OAuth auth succeeds without an API key when the user has none", async () => { const auth = await resolveAuth( { authorization: "Bearer valid-token" }, @@ -86,23 +134,82 @@ test("OAuth auth succeeds without an API key when the user has none", async () = assert.equal(auth.apiKey, undefined); }); -test("invalid Bearer token is rejected before API-key fallback", async () => { +function noFallback() { + return dependencies({ + validateBearerToken: async () => { + throw new Error("token validation must not run"); + }, + getApiKeyUsingKeyId: async () => { + throw new Error("API key fallback must not run"); + }, + }); +} + +test("Bearer token plus API key is ambiguous and validates neither", async () => { const auth = await resolveAuth( { authorization: "Bearer invalid-token", apiKeyHeader: "submitted-key", }, - dependencies({ - validateBearerToken: async () => null, - getApiKeyUsingKeyId: async () => { - throw new Error("API key fallback must not run"); - }, - }), + noFallback(), + ); + + assert.deepEqual(auth, { status: "ambiguous" }); +}); + +test("Bearer token plus a body API key is ambiguous", async () => { + const auth = await resolveAuth( + { authorization: "Bearer valid-token", bodyApiKey: "submitted-key" }, + noFallback(), + ); + + assert.deepEqual(auth, { status: "ambiguous" }); +}); + +test("invalid Bearer token is rejected", async () => { + const auth = await resolveAuth( + { authorization: "Bearer invalid-token" }, + dependencies({ validateBearerToken: async () => null }), ); assert.deepEqual(auth, { status: "invalid_token" }); }); +test("malformed Authorization never falls back to the API key", async () => { + for (const authorization of [ + "Basic dXNlcjpwYXNz", + "Bearer", + "Bearer a b", + ]) { + const auth = await resolveAuth( + { authorization, apiKeyHeader: "submitted-key" }, + noFallback(), + ); + + assert.deepEqual(auth, { status: "invalid_token" }, authorization); + } +}); + +test("different header and body API keys are ambiguous", async () => { + const auth = await resolveAuth( + { apiKeyHeader: "header-key", bodyApiKey: "body-key" }, + noFallback(), + ); + + assert.deepEqual(auth, { status: "ambiguous" }); +}); + +test("the same API key in header and body is one credential", async () => { + const auth = await resolveAuth( + { apiKeyHeader: "submitted-key", bodyApiKey: "submitted-key" }, + dependencies({}), + ); + + assert.equal(auth.status, "authenticated"); + assert.equal(auth.kind, "apikey"); + assert.equal(auth.apiKey, "submitted-key"); +}); + test("OAuth auth rejects a token for a missing user", async () => { const auth = await resolveAuth( { authorization: "Bearer valid-token" }, @@ -131,3 +238,55 @@ test("missing auth returns missing", async () => { assert.deepEqual(auth, { status: "missing" }); }); + +test("ambiguous credentials return the Platform 400 error", () => { + let status = 0; + let body: unknown; + const response = { + setHeader() {}, + status(code: number) { + status = code; + return this; + }, + json(value: unknown) { + body = value; + return this; + }, + }; + + assert.equal(sendAuthError(response, { status: "ambiguous" }), true); + assert.equal(status, 400); + assert.deepEqual(body, { + error: "credential_ambiguous", + error_description: "Exactly one credential mechanism is allowed.", + }); +}); + +test("MCP auth errors advertise protected-resource metadata", () => { + const headers = new Map(); + const response = { + statusCode: 200, + setHeader(name: string, value: string) { + headers.set(name, value); + }, + status(code: number) { + this.statusCode = code; + return this; + }, + json(_body: unknown) { + return this; + }, + }; + const metadataUrl = + "http://localhost:8000/.well-known/oauth-protected-resource/mcp"; + + assert.equal( + sendAuthError(response, { status: "missing" }, metadataUrl), + true, + ); + assert.equal(response.statusCode, 401); + assert.equal( + headers.get("WWW-Authenticate"), + `Bearer resource_metadata="${metadataUrl}"`, + ); +}); diff --git a/apps/api/src/auth/__tests__/scopes.test.ts b/apps/api/src/auth/__tests__/scopes.test.ts new file mode 100644 index 00000000..088d4e69 --- /dev/null +++ b/apps/api/src/auth/__tests__/scopes.test.ts @@ -0,0 +1,153 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { requireScope, toolScope } from "../scopes.js"; +import { createToolRegistrar, registerAllTools } from "../../mcp/server.js"; + +const READ_ONLY_TOOLS = new Set([ + "list_media", + "get_media", + "get_media_count", + "get_total_storage", + "get_media_settings", + "whoami", +]); + +type Registered = { + name: string; + call: (extra: any) => any; +}; + +// Calls handlers the way the MCP SDK does: tools without an input schema get +// only the request extra. +function fakeServer() { + const tools: Registered[] = []; + const server: any = { + registerTool( + name: string, + config: { inputSchema?: unknown }, + handler: any, + ) { + tools.push({ + name, + call: (extra) => + config.inputSchema === undefined + ? handler(extra) + : handler({}, extra), + }); + }, + }; + return { server, tools }; +} + +test("only tools marked read-only get the read scope", () => { + assert.equal(toolScope(true), "data:read"); + assert.equal(toolScope(false), "data:write"); + assert.equal(toolScope(undefined), "data:write"); +}); + +test("every MCP tool rejects a token without its scope", async () => { + const { server, tools } = fakeServer(); + registerAllTools(server); + + assert.equal(tools.length, 11); + for (const tool of tools) { + const scope = READ_ONLY_TOOLS.has(tool.name) + ? "data:read" + : "data:write"; + const result = await tool.call({ authInfo: { scopes: [] } }); + assert.equal(result.isError, true, tool.name); + assert.match(result.content[0].text, new RegExp(scope), tool.name); + } +}); + +test("a read-only token can read but not write", async () => { + const { server, tools } = fakeServer(); + const registrar = createToolRegistrar(server); + const calls: string[] = []; + registrar.registerTool( + "read", + { annotations: { readOnlyHint: true } }, + () => calls.push("read"), + ); + registrar.registerTool( + "write", + { annotations: { readOnlyHint: false } }, + () => calls.push("write"), + ); + const extra = { authInfo: { scopes: ["data:read"] } }; + + for (const tool of tools) await tool.call(extra); + + assert.deepEqual(calls, ["read"]); +}); + +test("tools without input get the request extra as their second argument", async () => { + const { server, tools } = fakeServer(); + const registrar = createToolRegistrar(server); + const received: unknown[] = []; + registrar.registerTool( + "no_input", + { annotations: { readOnlyHint: true } }, + (args, extra) => received.push(args, extra), + ); + registrar.registerTool( + "with_input", + { inputSchema: {}, annotations: { readOnlyHint: true } }, + (args, extra) => received.push(args, extra), + ); + const extra = { authInfo: { scopes: ["data:read"] } }; + + for (const tool of tools) await tool.call(extra); + + assert.deepEqual(received, [{}, extra, {}, extra]); +}); + +function response() { + return { + statusCode: 200, + headers: {} as Record, + body: undefined as unknown, + setHeader(name: string, value: string) { + this.headers[name] = value; + }, + status(code: number) { + this.statusCode = code; + return this; + }, + json(body: unknown) { + this.body = body; + return this; + }, + }; +} + +test("REST routes reject a token without the required scope", () => { + const res = response(); + let nextCalled = false; + requireScope("data:write")({ scopes: ["data:read"] }, res as any, () => { + nextCalled = true; + }); + + assert.equal(nextCalled, false); + assert.equal(res.statusCode, 403); + assert.equal( + res.headers["WWW-Authenticate"], + 'Bearer error="insufficient_scope", scope="data:write"', + ); + assert.deepEqual( + (res.body as { error: string }).error, + "insufficient_scope", + ); +}); + +test("REST routes pass a token with the required scope", () => { + let nextCalled = false; + requireScope("data:read")( + { scopes: ["data:read"] }, + response() as any, + () => { + nextCalled = true; + }, + ); + assert.equal(nextCalled, true); +}); diff --git a/apps/api/src/auth/bearer.ts b/apps/api/src/auth/bearer.ts new file mode 100644 index 00000000..116986fa --- /dev/null +++ b/apps/api/src/auth/bearer.ts @@ -0,0 +1,68 @@ +import { verifyOAuthAccessToken } from "@codelitdev/oauth-server-kit"; +import type { MedialitAuth } from "./better-auth"; +import { APP_ID_CLAIM } from "./oauth-app-selection"; + +export type BearerClaims = { + userId: string; + clientId: string; + scopes: string[]; + /** The app picked at authorization; absent on tokens issued before the + * app picker existed. */ + appId?: string; +}; + +/** Reads a string claim from a token the kit has already verified. The kit's + * identity omits custom claims, and opaque tokens carry none. */ +function verifiedJwtClaim(token: string, name: string): string | undefined { + const parts = token.split("."); + if (parts.length !== 3) return undefined; + try { + const payload = JSON.parse( + Buffer.from(parts[1], "base64url").toString("utf8"), + ); + const value = payload?.[name]; + return typeof value === "string" && value.length > 0 + ? value + : undefined; + } catch { + return undefined; + } +} + +let kitAuth: MedialitAuth | null = null; + +export function setBearerAuth(auth: MedialitAuth): void { + kitAuth = auth; +} + +export function getMcpResourceMetadataUrl(): string | undefined { + return kitAuth + ? `${kitAuth.publicApiUrl}/.well-known/oauth-protected-resource/mcp` + : undefined; +} + +export async function validateBearerToken( + bearer: string, +): Promise { + if (!kitAuth) return null; + const result = await verifyOAuthAccessToken( + { + oauthResourceClient: kitAuth.oauthResourceClient, + issuer: kitAuth.issuer, + audiences: [ + kitAuth.restResource, + kitAuth.mcpResource, + kitAuth.publicApiUrl, + ], + }, + bearer, + ); + if (result.status !== "authenticated") return null; + const { identity } = result; + return { + userId: identity.subject, + clientId: identity.method === "oauth" ? identity.clientId : "", + scopes: [...identity.scopes], + appId: verifiedJwtClaim(bearer, APP_ID_CLAIM), + }; +} diff --git a/apps/api/src/auth/better-auth.ts b/apps/api/src/auth/better-auth.ts new file mode 100644 index 00000000..2ce235a2 --- /dev/null +++ b/apps/api/src/auth/better-auth.ts @@ -0,0 +1,37 @@ +import { oauthProviderResourceClient } from "@better-auth/oauth-provider/resource-client"; +import { betterAuth } from "better-auth"; +import { drizzleAdapter } from "better-auth/adapters/drizzle"; +import { getDb } from "@/db"; +import * as authSchema from "@/db/schema/auth.generated"; +import { AUTH_BASE_PATH, authUrls, medialitAuthOptions } from "./options"; + +export function createMedialitAuth(input: { + publicApiUrl: string; + secret: string; + webOrigin?: string; +}) { + if (input.secret.length < 32) { + throw new Error("Auth secret must be at least 32 characters"); + } + const urls = authUrls(input.publicApiUrl, input.webOrigin); + const auth = betterAuth( + medialitAuthOptions({ + publicApiUrl: input.publicApiUrl, + secret: input.secret, + webOrigin: input.webOrigin, + database: drizzleAdapter(getDb(), { + provider: "pg", + schema: authSchema, + }), + }), + ); + const oauthResourceClient = oauthProviderResourceClient(auth); + return { + auth, + oauthResourceClient, + ...urls, + authBasePath: AUTH_BASE_PATH, + }; +} + +export type MedialitAuth = ReturnType; diff --git a/apps/api/src/auth/cimd-fetch.ts b/apps/api/src/auth/cimd-fetch.ts new file mode 100644 index 00000000..0d77be86 --- /dev/null +++ b/apps/api/src/auth/cimd-fetch.ts @@ -0,0 +1,95 @@ +import type { ClientMetadataResourceFetch } from "@better-auth/oauth-provider"; +import { fetchClientMetadataResource as fetchNodeClientMetadataResource } from "@better-auth/cimd/node"; + +const ignoredGrantTypesByClientMetadataUrl = new Map>([ + [ + "https://vscode.dev/oauth/client-metadata.json", + new Set(["urn:ietf:params:oauth:grant-type:device_code"]), + ], + [ + "https://claude.ai/oauth/mcp-oauth-client-metadata", + new Set(["urn:ietf:params:oauth:grant-type:jwt-bearer"]), + ], +]); + +const maximumMetadataBytes = 5 * 1024; + +async function readBoundedMetadata( + response: Response, +): Promise> { + const reader = response.body?.getReader(); + if (!reader) return new Uint8Array(new ArrayBuffer(0)); + const chunks: Uint8Array[] = []; + let size = 0; + try { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + size += value.byteLength; + if (size > maximumMetadataBytes) { + await reader.cancel(); + throw new TypeError("CIMD metadata response is too large"); + } + chunks.push(value); + } + } finally { + reader.releaseLock(); + } + const body = new Uint8Array(new ArrayBuffer(size)); + let offset = 0; + for (const chunk of chunks) { + body.set(chunk, offset); + offset += chunk.byteLength; + } + return body; +} + +/** + * VS Code and Claude advertise optional grants that MediaLit's OAuth provider + * does not implement. Keep these exceptions exact; the upstream transport and + * provider retain their normal security and metadata validation for all clients. + */ +export const fetchClientMetadataResource: ClientMetadataResourceFetch = async ( + input, + init, +) => { + const response = await fetchNodeClientMetadataResource(input, init); + const requestUrl = input instanceof Request ? input.url : String(input); + const ignoredGrantTypes = ignoredGrantTypesByClientMetadataUrl.get( + new URL(requestUrl).href, + ); + if (!ignoredGrantTypes || response.status !== 200) return response; + + const body = await readBoundedMetadata(response); + const metadata = JSON.parse(new TextDecoder().decode(body)) as { + grant_types?: unknown; + [key: string]: unknown; + }; + if (!Array.isArray(metadata.grant_types)) { + const responseHeaders = new Headers(response.headers); + responseHeaders.delete("content-length"); + return new Response(body, { + headers: responseHeaders, + status: response.status, + statusText: response.statusText, + }); + } + + const responseHeaders = new Headers(response.headers); + responseHeaders.delete("content-length"); + return new Response( + JSON.stringify({ + ...metadata, + grant_types: metadata.grant_types.filter( + (grantType) => + typeof grantType !== "string" || + !ignoredGrantTypes.has(grantType), + ), + }), + { + headers: responseHeaders, + status: response.status, + statusText: response.statusText, + }, + ); +}; diff --git a/apps/api/src/auth/middleware.ts b/apps/api/src/auth/middleware.ts index 7553b3c2..f7e8dac6 100644 --- a/apps/api/src/auth/middleware.ts +++ b/apps/api/src/auth/middleware.ts @@ -1,5 +1,7 @@ import { NextFunction, Response } from "express"; +import { getMcpResourceMetadataUrl } from "./bearer"; import { AuthResult, resolveAuth, sendAuthError } from "./resolve-auth"; +import { API_KEY_SCOPES } from "./scopes"; type AuthResolver = (input: { authorization?: unknown; @@ -18,12 +20,13 @@ function applyAuthToRequest( req.user = auth.user; req.apikey = auth.apiKey; + req.scopes = auth.kind === "oauth" ? auth.scopes : API_KEY_SCOPES; if (mode === "mcp") { + req.authKind = auth.kind; req.userId = auth.userId; if (auth.kind === "oauth") { req.clientId = auth.clientId; - req.scopes = auth.scopes; } } } @@ -43,7 +46,14 @@ export function createAuthMiddleware( bodyApiKey: req.body?.apikey, }); - if (sendAuthError(res, auth)) return; + if ( + sendAuthError( + res, + auth, + mode === "mcp" ? getMcpResourceMetadataUrl() : undefined, + ) + ) + return; if (auth.status !== "authenticated") return; applyAuthToRequest(req, auth, mode); diff --git a/apps/api/src/auth/oauth-app-pages.ts b/apps/api/src/auth/oauth-app-pages.ts new file mode 100644 index 00000000..1697ec66 --- /dev/null +++ b/apps/api/src/auth/oauth-app-pages.ts @@ -0,0 +1,161 @@ +import { fromNodeHeaders } from "better-auth/node"; +import express, { + Router, + type NextFunction, + type Request, + type RequestHandler, + type Response, +} from "express"; +import type { OAuthApp, OAuthAppSelectionAdapter } from "./oauth-app-selection"; + +export const SELECT_APP_PATH = "/oauth/select-app"; + +type SessionApi = { + api: { + getSession(input: { headers: Headers }): Promise<{ + user: { id: string }; + session: { id: string }; + } | null>; + }; +}; + +function escapeHtml(value: string): string { + return value + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); +} + +// Mirrors the hosted login and consent pages from oauth-server-kit. +const STYLES = `html{--brand-primary:#8c7a6b;color-scheme:light}*{box-sizing:border-box}body{margin:0;min-height:100vh;display:grid;place-items:center;padding:24px;background:#f4f4f5;color:#171717;font-family:system-ui,sans-serif} +.card{width:100%;max-width:400px;padding:32px;border:1px solid #e4e4e7;border-radius:14px;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.04)}.logo{width:38px;height:38px;margin:0 auto 20px;display:grid;place-items:center;border-radius:50%;background:#171717;color:#fff;font-weight:700} +h1{margin:0 0 24px;text-align:center;font-size:22px;font-weight:650}.sub{margin:-12px 0 24px;text-align:center;color:#71717a;font-size:13px;line-height:1.5}.error{display:none;margin-bottom:16px;padding:10px 12px;border-radius:8px;background:#fef2f2;color:#b42318;font-size:13px} +form{margin:0}.apps{display:grid;gap:8px;margin-bottom:16px}.app{display:flex;align-items:center;gap:10px;padding:12px 14px;border:1px solid #e4e4e7;border-radius:9px;background:#fafafa;cursor:pointer;font-size:14px}.app:has(input:checked){border-color:var(--brand-primary);background:#fff}.app input{margin:0;accent-color:var(--brand-primary)}.app-name{flex:1;font-weight:550;word-break:break-word}.badge{padding:2px 6px;border:1px solid #e4e4e7;border-radius:6px;color:#52525b;font-size:11px} +button{width:100%;margin-top:8px;padding:11px;border:1px solid var(--brand-primary);border-radius:8px;background:var(--brand-primary);color:#fff;font-size:14px;font-weight:550;cursor:pointer}button:focus-visible{outline:2px solid var(--brand-primary);outline-offset:2px}button:disabled{opacity:.55;cursor:not-allowed}`; + +function page(body: string): string { + return `Choose a MediaLit app
${body}
`; +} + +/** + * A JavaScript string literal that is safe inside an inline " and line separators intact. + */ +function scriptString(value: string): string { + return JSON.stringify(value) + .replace(/ app.isDefault) ?? apps[0]; + const choices = apps + .map( + (app) => + ``, + ) + .join(""); + return page( + `

Choose an app

The connected client will upload, list and delete files in this app.

${choices}
`, + ); +} + +function asyncHandler( + handler: (request: Request, response: Response) => Promise, +): RequestHandler { + return (request, response, next: NextFunction) => { + void handler(request, response).catch(next); + }; +} + +/** + * The post-login app picker. Better Auth redirects here (see + * `oauth-app-selection.ts`) when the user has more than one app and has not + * picked one in this authorization flow. + */ +export function createOAuthAppPages(options: { + auth: SessionApi; + adapter: OAuthAppSelectionAdapter; + authBasePath: string; +}): Router { + const router = Router(); + + router.use(SELECT_APP_PATH, (_request, response, next) => { + response.setHeader("Content-Security-Policy", "frame-ancestors 'none'"); + response.setHeader("X-Frame-Options", "DENY"); + response.setHeader("X-Content-Type-Options", "nosniff"); + response.setHeader("Cache-Control", "no-store"); + next(); + }); + + router.get( + SELECT_APP_PATH, + asyncHandler(async (request, response) => { + const session = await options.auth.api.getSession({ + headers: fromNodeHeaders(request.headers), + }); + const oauthQuery = request.originalUrl.split("?")[1] ?? ""; + if (!session?.user) { + response.redirect(`/oauth/login?${oauthQuery}`); + return; + } + const apps = await options.adapter.listAppsForUser(session.user.id); + if (apps.length <= 1) { + response + .type("html") + .send( + page( + `

Continuing…

`, + ), + ); + return; + } + response.type("html").send(pickerPage(apps, options.authBasePath)); + }), + ); + + router.post( + SELECT_APP_PATH, + express.json(), + asyncHandler(async (request, response) => { + const session = await options.auth.api.getSession({ + headers: fromNodeHeaders(request.headers), + }); + if (!session?.user) { + response.status(401).json({ error: "unauthorized" }); + return; + } + const appId = + typeof request.body?.appId === "string" + ? request.body.appId + : undefined; + if (!appId) { + response.status(400).json({ + error: "invalid_request", + error_description: "appId is required", + }); + return; + } + const apps = await options.adapter.listAppsForUser(session.user.id); + const selected = apps.find((app) => app.keyId === appId); + if (!selected) { + response.status(404).json({ + error: "app_not_found", + error_description: "This app does not exist.", + }); + return; + } + await options.adapter.setSelection(session.session.id, selected.id); + response.json({ ok: true }); + }), + ); + + return router; +} diff --git a/apps/api/src/auth/oauth-app-selection.ts b/apps/api/src/auth/oauth-app-selection.ts new file mode 100644 index 00000000..37b78443 --- /dev/null +++ b/apps/api/src/auth/oauth-app-selection.ts @@ -0,0 +1,112 @@ +import { getOAuthAppSelection, listApiKeys, setOAuthAppSelection } from "@/db"; + +export const APP_ID_CLAIM = "app_id"; + +// The picker shows on every new authorization. A selection only skips it for +// the rest of the flow it was made in, which the authorization code lifetime +// bounds (Better Auth's default `codeExpiresIn` is 10 minutes). +const SELECTION_FRESH_MS = 10 * 60 * 1000; + +export type OAuthApp = { + id: string; + keyId: string; + name: string; + isDefault: boolean; +}; + +export interface OAuthAppSelectionAdapter { + listAppsForUser(userId: string): Promise; + getSelection( + sessionId: string, + ): Promise<{ keyId: string; updatedAt: Date } | null>; + setSelection(sessionId: string, apiKeyId: string): Promise; +} + +export const oauthAppSelectionAdapter: OAuthAppSelectionAdapter = { + async listAppsForUser(userId) { + const keys = await listApiKeys(userId); + return keys.map((key) => ({ + id: key.id, + keyId: key.keyId, + name: key.name, + isDefault: Boolean(key.default), + })); + }, + getSelection: getOAuthAppSelection, + setSelection: setOAuthAppSelection, +}; + +async function resolveSelection( + adapter: OAuthAppSelectionAdapter, + userId: string, + sessionId: string, + now: number, +) { + const apps = await adapter.listAppsForUser(userId); + // A single app is selected without asking. + if (apps.length === 1) { + return { requiresSelection: false, keyId: apps[0].keyId }; + } + if (apps.length === 0) { + return { requiresSelection: false, keyId: undefined }; + } + const selection = await adapter.getSelection(sessionId); + const valid = selection + ? apps.some((app) => app.keyId === selection.keyId) + : false; + const fresh = + valid && now - selection!.updatedAt.getTime() < SELECTION_FRESH_MS; + return { + requiresSelection: !fresh, + keyId: valid ? selection!.keyId : undefined, + }; +} + +/** + * `oauthProvider` options that send the user to the app picker after sign-in + * and put the picked app on the access token. The app is the consent + * `referenceId`, so consent is recorded per app. + */ +export function createOAuthAppSelectionHooks(options: { + page: string; + adapter: OAuthAppSelectionAdapter; + now?: () => number; +}) { + const now = options.now ?? Date.now; + return { + postLogin: { + page: options.page, + async shouldRedirect(input: { + user: { id: string }; + session: { id: string }; + }) { + return ( + await resolveSelection( + options.adapter, + input.user.id, + input.session.id, + now(), + ) + ).requiresSelection; + }, + async consentReferenceId(input: { + user: { id: string }; + session: { id: string }; + }) { + return ( + await resolveSelection( + options.adapter, + input.user.id, + input.session.id, + now(), + ) + ).keyId; + }, + }, + async customAccessTokenClaims(input: { referenceId?: string | null }) { + return input.referenceId + ? { [APP_ID_CLAIM]: input.referenceId } + : {}; + }, + }; +} diff --git a/apps/api/src/auth/options.ts b/apps/api/src/auth/options.ts new file mode 100644 index 00000000..d2a69b4b --- /dev/null +++ b/apps/api/src/auth/options.ts @@ -0,0 +1,135 @@ +import { oauthProvider } from "@better-auth/oauth-provider"; +import { cimd } from "@better-auth/cimd"; +import { createOAuthProviderOptions } from "@codelitdev/oauth-server-kit/better-auth"; +import type { BetterAuthOptions } from "better-auth"; +import { emailOTP } from "better-auth/plugins/email-otp"; +import { jwt } from "better-auth/plugins/jwt"; +import { ensureProfile } from "@/db"; +import { sendSignInCode } from "../services/mail"; +import logger from "../services/log"; +import { fetchClientMetadataResource } from "./cimd-fetch"; +import { SELECT_APP_PATH } from "./oauth-app-pages"; +import { + createOAuthAppSelectionHooks, + oauthAppSelectionAdapter, +} from "./oauth-app-selection"; + +export const AUTH_BASE_PATH = "/api/auth"; +export const AUTH_COOKIE_PREFIX = "medialit"; +export const DATA_READ_SCOPE = "data:read"; +export const DATA_WRITE_SCOPE = "data:write"; +// Advertised to MCP clients, which request exactly these scopes. +// `offline_access` gets them a refresh token instead of re-consenting +// every time the access token expires. +export const MCP_SCOPES_SUPPORTED = [ + DATA_READ_SCOPE, + DATA_WRITE_SCOPE, + "offline_access", +] as const; + +export function authUrls(publicApiUrl: string, webOrigin?: string) { + const normalized = publicApiUrl.replace(/\/$/, ""); + return { + publicApiUrl: normalized, + webOrigin: (webOrigin ?? normalized).replace(/\/$/, ""), + issuer: `${normalized}${AUTH_BASE_PATH}`, + restResource: `${normalized}/media`, + mcpResource: `${normalized}/mcp`, + }; +} + +export function medialitAuthOptions(input: { + publicApiUrl: string; + secret: string; + webOrigin?: string; + database?: BetterAuthOptions["database"]; +}): BetterAuthOptions { + // The return type stops `betterAuth()` from inferring a unique plugin + // type. That inference exhausts the typechecker heap. + const urls = authUrls(input.publicApiUrl, input.webOrigin); + const extraOrigins = (process.env.AUTH_TRUSTED_ORIGINS ?? "") + .split(",") + .map((origin) => origin.trim()) + .filter(Boolean); + return { + appName: "MediaLit", + baseURL: urls.publicApiUrl, + basePath: AUTH_BASE_PATH, + secret: input.secret, + trustedOrigins: [urls.webOrigin, urls.publicApiUrl, ...extraOrigins], + advanced: { + cookiePrefix: AUTH_COOKIE_PREFIX, + }, + emailAndPassword: { enabled: false }, + ...(input.database ? { database: input.database } : {}), + databaseHooks: { + user: { + create: { + after: async (created: { id: string }) => { + try { + await ensureProfile({ userId: created.id }); + } catch (error) { + logger.error( + { error }, + "Failed to provision user profile", + ); + } + }, + }, + }, + }, + plugins: [ + jwt(), + emailOTP({ + async sendVerificationOTP({ email, otp }) { + await sendSignInCode(email, otp); + }, + }), + oauthProvider({ + ...createOAuthProviderOptions({ + loginPage: `${urls.publicApiUrl}/oauth/login`, + consentPage: `${urls.publicApiUrl}/oauth/consent`, + scopes: [ + "openid", + "profile", + "email", + "offline_access", + DATA_READ_SCOPE, + DATA_WRITE_SCOPE, + ], + validAudiences: [ + urls.restResource, + urls.mcpResource, + urls.publicApiUrl, + ], + allowDynamicClientRegistration: true, + allowUnauthenticatedDynamicClientRegistration: true, + clientRegistrationDefaultScopes: [ + "openid", + "profile", + "email", + ], + clientRegistrationAllowedScopes: [ + "offline_access", + DATA_READ_SCOPE, + DATA_WRITE_SCOPE, + ], + }), + clientRegistrationDefaultResources: [urls.mcpResource], + ...createOAuthAppSelectionHooks({ + page: `${urls.publicApiUrl}${SELECT_APP_PATH}`, + adapter: oauthAppSelectionAdapter, + }), + }), + cimd({ + fetchClientMetadataResource, + metadataProfile: "mcp-2026-07-28", + metadataRevalidationInterval: "60m", + metadataFetchPolicy: { + minimumFetchInterval: 0, + }, + originBoundFields: ["post_logout_redirect_uris", "client_uri"], + }), + ], + }; +} diff --git a/apps/api/src/auth/resolve-auth.ts b/apps/api/src/auth/resolve-auth.ts index dc79bcb8..0590856d 100644 --- a/apps/api/src/auth/resolve-auth.ts +++ b/apps/api/src/auth/resolve-auth.ts @@ -1,6 +1,12 @@ +import { + PLATFORM_ERROR_HTTP_STATUS, + PLATFORM_ERROR_MESSAGES, + type PresentedCredential, + selectSingleCredential, +} from "@codelitdev/platform"; import { Apikey } from "@medialit/models"; import { getApiKeyByUserId, getApiKeyUsingKeyId } from "../apikey/queries"; -import { validateBearerToken } from "../oauth/middleware"; +import { validateBearerToken } from "./bearer"; import { getUser } from "../user/queries"; type UserRecord = any; @@ -9,6 +15,7 @@ type OAuthClaims = { userId: string; clientId: string; scopes: string[]; + appId?: string; }; export type AuthInput = { @@ -20,7 +27,10 @@ export type AuthInput = { export type AuthDependencies = { validateBearerToken: (bearer: string) => Promise; getUser: (id: string) => Promise; - getApiKeyByUserId: (userId: string) => Promise; + getApiKeyByUserId: ( + userId: string, + keyId?: string, + ) => Promise; getApiKeyUsingKeyId: (key: string) => Promise; }; @@ -43,9 +53,20 @@ export type AuthResult = } | { status: "invalid_token" } | { status: "unauthorized" } + | { status: "ambiguous" } | { status: "missing" }; -export function sendAuthError(res: any, auth: AuthResult): boolean { +export function sendAuthError( + res: any, + auth: AuthResult, + resourceMetadataUrl?: string, +): boolean { + if (auth.status !== "authenticated" && resourceMetadataUrl) { + res.setHeader( + "WWW-Authenticate", + `Bearer resource_metadata="${resourceMetadataUrl}"`, + ); + } if (auth.status === "invalid_token") { res.status(401).json({ error: "invalid_token", @@ -68,6 +89,14 @@ export function sendAuthError(res: any, auth: AuthResult): boolean { return true; } + if (auth.status === "ambiguous") { + res.status(PLATFORM_ERROR_HTTP_STATUS.credential_ambiguous).json({ + error: "credential_ambiguous", + error_description: PLATFORM_ERROR_MESSAGES.credential_ambiguous, + }); + return true; + } + return false; } @@ -93,11 +122,23 @@ export function selectEffectiveApiKey( return keys.find((key) => key.default === true) || keys[0] || null; } +/** + * The app an OAuth token acts on: the one picked at authorization, or the + * user's default app for tokens issued before the picker existed. `null` + * means the picked app no longer exists. + */ async function getEffectiveOAuthApiKey( userId: string, + appId: string | undefined, dependencies: AuthDependencies, -): Promise { +): Promise { try { + if (appId) { + const key = selectEffectiveApiKey( + await dependencies.getApiKeyByUserId(userId, appId), + ); + return key && key.keyId === appId ? key.key : null; + } const keys = await dependencies.getApiKeyByUserId(userId); return selectEffectiveApiKey(keys)?.key; } catch { @@ -105,40 +146,75 @@ async function getEffectiveOAuthApiKey( } } +/** + * Follows the Platform credential contract (ADR 0001): a request carries + * exactly one mechanism, and a malformed or invalid credential is rejected + * rather than falling back to another one. + */ export async function resolveAuth( input: AuthInput, dependencies: AuthDependencies = defaultDependencies, ): Promise { const authorization = getHeaderValue(input.authorization); + const headerApiKey = getHeaderValue(input.apiKeyHeader); + const bodyApiKey = getHeaderValue(input.bodyApiKey); + + let bearer: string | undefined; if (authorization) { - const match = authorization.match(/^Bearer (.+)$/i); - if (match) { - const claims = await dependencies.validateBearerToken(match[1]); - if (!claims) return { status: "invalid_token" }; - - const user = await dependencies.getUser(claims.userId); - if (!user) return { status: "unauthorized" }; - - return { - status: "authenticated", - kind: "oauth", - user, - userId: claims.userId, - clientId: claims.clientId, - scopes: claims.scopes, - apiKey: await getEffectiveOAuthApiKey( - claims.userId, - dependencies, - ), - }; - } + const match = /^Bearer\s+(\S+)$/i.exec(authorization.trim()); + if (!match) return { status: "invalid_token" }; + bearer = match[1]; + } + // The header and the legacy body field are one mechanism; two different + // keys are ambiguous. + if (headerApiKey && bodyApiKey && headerApiKey !== bodyApiKey) { + return { status: "ambiguous" }; } - const submittedApiKey = - getHeaderValue(input.bodyApiKey) || getHeaderValue(input.apiKeyHeader); - if (!submittedApiKey) return { status: "missing" }; + const presented: PresentedCredential[] = []; + if (bearer) presented.push({ kind: "oauth", secret: bearer }); + const submittedApiKey = bodyApiKey || headerApiKey; + if (submittedApiKey) { + presented.push({ kind: "api_key", secret: submittedApiKey }); + } + const selection = selectSingleCredential(presented); + if (selection.kind === "absent") return { status: "missing" }; + if (selection.kind === "ambiguous") return { status: "ambiguous" }; + // The header was already parsed above; treat a malformed selection the + // same way as a malformed Authorization header. + if (selection.kind === "malformed") return { status: "invalid_token" }; + + if (selection.credential.kind === "oauth") { + const claims = await dependencies.validateBearerToken( + selection.credential.secret, + ); + if (!claims) return { status: "invalid_token" }; + + const user = await dependencies.getUser(claims.userId); + if (!user) return { status: "unauthorized" }; + + const apiKey = await getEffectiveOAuthApiKey( + claims.userId, + claims.appId, + dependencies, + ); + // The picked app was deleted; the client must re-authorize and pick + // another rather than silently act on a different app. + if (apiKey === null) return { status: "invalid_token" }; + + return { + status: "authenticated", + kind: "oauth", + user, + userId: claims.userId, + clientId: claims.clientId, + scopes: claims.scopes, + apiKey, + }; + } - const apiKey = await dependencies.getApiKeyUsingKeyId(submittedApiKey); + const submitted = selection.credential.secret; + const apiKey = await dependencies.getApiKeyUsingKeyId(submitted); if (!apiKey) return { status: "unauthorized" }; const userId = apiKey.userId.toString(); @@ -150,6 +226,6 @@ export async function resolveAuth( kind: "apikey", user, userId, - apiKey: submittedApiKey, + apiKey: submitted, }; } diff --git a/apps/api/src/auth/scopes.ts b/apps/api/src/auth/scopes.ts new file mode 100644 index 00000000..c738bfb2 --- /dev/null +++ b/apps/api/src/auth/scopes.ts @@ -0,0 +1,44 @@ +import type { NextFunction, Response } from "express"; +import { DATA_READ_SCOPE, DATA_WRITE_SCOPE } from "./options"; + +export type DataScope = typeof DATA_READ_SCOPE | typeof DATA_WRITE_SCOPE; + +// API keys predate scopes and keep full access to their account. +export const API_KEY_SCOPES: readonly string[] = [ + DATA_READ_SCOPE, + DATA_WRITE_SCOPE, +]; + +/** MCP tools declare `readOnlyHint`; anything not read-only needs write. */ +export function toolScope(readOnlyHint: boolean | undefined): DataScope { + return readOnlyHint === true ? DATA_READ_SCOPE : DATA_WRITE_SCOPE; +} + +export function hasScope( + scopes: readonly string[] | undefined, + scope: DataScope, +): boolean { + return Boolean(scopes?.includes(scope)); +} + +export function insufficientScopeMessage(scope: DataScope): string { + return `This token lacks the ${scope} scope. Re-authorize the client to grant it.`; +} + +/** Run after the auth middleware, which sets `req.scopes`. */ +export function requireScope(scope: DataScope) { + return (req: any, res: Response, next: NextFunction): void => { + if (hasScope(req.scopes, scope)) { + next(); + return; + } + res.setHeader( + "WWW-Authenticate", + `Bearer error="insufficient_scope", scope="${scope}"`, + ); + res.status(403).json({ + error: "insufficient_scope", + error_description: insufficientScopeMessage(scope), + }); + }; +} diff --git a/apps/api/src/billing/authorization.ts b/apps/api/src/billing/authorization.ts new file mode 100644 index 00000000..ea31d461 --- /dev/null +++ b/apps/api/src/billing/authorization.ts @@ -0,0 +1,36 @@ +import { systemClock } from "@codelitdev/billing/core"; +import { BillingWorkflowError } from "@codelitdev/billing/core"; +import type { + BillingAction, + BillingActionGrant, + BillingAuthorizationPort, +} from "@codelitdev/billing/workflows"; + +export const medialitBillingAuthorization: BillingAuthorizationPort = { + async consume(grant, expectedAction, expectedTarget, now) { + if ( + !grant.grantId.startsWith("preconsumed:") || + grant.action !== expectedAction || + grant.target.kind !== expectedTarget.kind || + grant.target.id !== expectedTarget.id || + grant.expiresAt.getTime() <= now.getTime() + ) { + throw new BillingWorkflowError("grant_invalid"); + } + }, +}; + +export function preconsumedGrant( + action: BillingAction, + userId: string, +): BillingActionGrant { + const now = systemClock.now(); + return { + grantId: `preconsumed:${action}:${userId}:${now.getTime()}`, + actorId: userId, + action, + target: { kind: "user", id: userId }, + issuedAt: now, + expiresAt: new Date(now.getTime() + 5 * 60 * 1000), + }; +} diff --git a/apps/api/src/billing/catalog.ts b/apps/api/src/billing/catalog.ts new file mode 100644 index 00000000..53866288 --- /dev/null +++ b/apps/api/src/billing/catalog.ts @@ -0,0 +1,164 @@ +import { BillingConfigurationError } from "@codelitdev/billing/core"; +import { + validateCatalog, + type BillingOffer as PackageBillingOffer, +} from "@codelitdev/billing/catalog"; + +export const billingCatalogKeys = ["pro_month", "pro_year"] as const; +export type BillingCatalogKey = (typeof billingCatalogKeys)[number]; +export type BillingInterval = "month" | "year"; + +export const PRO_MONTH_OFFER_KEY = "pro_month"; +export const PRO_YEAR_OFFER_KEY = "pro_year"; + +export type BillingDeploymentMode = "oss" | "cloud"; + +export type CloudBillingConfig = { + deploymentMode: "cloud"; + catalogRevision: number; + currency: string; + offers: PackageBillingOffer[]; +}; + +export type BillingComposition = { deploymentMode: "oss" } | CloudBillingConfig; + +const offerEnv: Record< + BillingCatalogKey, + { amount: string; product: string; interval: BillingInterval } +> = { + pro_month: { + amount: "BILLING_PRO_MONTH_AMOUNT_MINOR", + product: "DODO_PRO_MONTH_PRODUCT_ID", + interval: "month", + }, + pro_year: { + amount: "BILLING_PRO_YEAR_AMOUNT_MINOR", + product: "DODO_PRO_YEAR_PRODUCT_ID", + interval: "year", + }, +}; + +const CLOUD_ENV = [ + "BILLING_CATALOG_REVISION", + "BILLING_CURRENCY", + "BILLING_PRO_MONTH_AMOUNT_MINOR", + "BILLING_PRO_YEAR_AMOUNT_MINOR", + "DODO_PRO_MONTH_PRODUCT_ID", + "DODO_PRO_YEAR_PRODUCT_ID", + "DODO_PAYMENTS_API_KEY", + "DODO_PAYMENTS_WEBHOOK_KEY_CURRENT", + "BILLING_DATA_ENCRYPTION_KEY", +] as const; + +export function deploymentMode( + env: NodeJS.ProcessEnv = process.env, +): BillingDeploymentMode { + return env.MEDIALIT_DEPLOYMENT_MODE === "oss" ? "oss" : "cloud"; +} + +export function proOfferKey(interval: BillingInterval): BillingCatalogKey { + return interval === "year" ? PRO_YEAR_OFFER_KEY : PRO_MONTH_OFFER_KEY; +} + +function required(env: NodeJS.ProcessEnv, name: string): string { + const value = env[name]; + if (value === undefined || value.length === 0) { + throw new BillingConfigurationError(`${name}_missing`); + } + if (value !== value.trim()) { + throw new BillingConfigurationError(`${name}_whitespace`); + } + return value; +} + +function positiveSafeInteger(value: string, name: string): number { + if (!/^(0|[1-9][0-9]*)$/.test(value)) { + throw new BillingConfigurationError(`${name}_must_be_decimal_integer`); + } + const parsed = Number(value); + if ( + !Number.isSafeInteger(parsed) || + parsed <= 0 || + parsed > 2_147_483_647 + ) { + throw new BillingConfigurationError(`${name}_out_of_range`); + } + return parsed; +} + +/** + * Paid Dodo catalog. Returns null for OSS and for a cloud process with no + * billing env vars. A partial cloud configuration throws. Quotas stay on the + * profile either way. + */ +export function readCloudBillingConfig( + env: NodeJS.ProcessEnv = process.env, +): CloudBillingConfig | null { + if (deploymentMode(env) === "oss") return null; + const present = CLOUD_ENV.filter((name) => { + const value = env[name]; + return value !== undefined && value.length > 0; + }); + if (present.length === 0) return null; + if (present.length !== CLOUD_ENV.length) { + throw new BillingConfigurationError("billing_cloud_config_incomplete"); + } + + const revision = positiveSafeInteger( + required(env, "BILLING_CATALOG_REVISION"), + "BILLING_CATALOG_REVISION", + ); + const currency = required(env, "BILLING_CURRENCY").toUpperCase(); + if (!/^[A-Z]{3}$/.test(currency)) { + throw new BillingConfigurationError("BILLING_CURRENCY_invalid"); + } + + const offers = billingCatalogKeys.map((key) => { + const definition = offerEnv[key]; + const providerProductId = required(env, definition.product); + if (!/^[^\s]{2,256}$/.test(providerProductId)) { + throw new BillingConfigurationError( + `${definition.product}_invalid`, + ); + } + return { + key, + revision, + plan: "pro", + interval: definition.interval, + currency, + amountMinor: positiveSafeInteger( + required(env, definition.amount), + definition.amount, + ), + provider: "dodo", + providerProductId, + providerTrialDays: 0, + } satisfies PackageBillingOffer; + }); + + const productIds = new Set(offers.map((offer) => offer.providerProductId)); + if (productIds.size !== offers.length) { + throw new BillingConfigurationError("provider_products_must_be_unique"); + } + + validateCatalog({ + offers, + requiredOfferKeys: [...billingCatalogKeys], + revision, + checkoutProvider: "dodo", + }); + return { + deploymentMode: "cloud", + catalogRevision: revision, + currency, + offers, + }; +} + +/** OSS, and cloud with no Dodo settings, compose an engine with no checkout. */ +export function billingComposition( + env: NodeJS.ProcessEnv = process.env, +): BillingComposition { + return readCloudBillingConfig(env) ?? { deploymentMode: "oss" }; +} diff --git a/apps/api/src/billing/crypto.ts b/apps/api/src/billing/crypto.ts new file mode 100644 index 00000000..22358737 --- /dev/null +++ b/apps/api/src/billing/crypto.ts @@ -0,0 +1,42 @@ +import crypto from "node:crypto"; + +const algorithm = "aes-256-gcm"; + +function key(): Buffer { + const raw = process.env.BILLING_DATA_ENCRYPTION_KEY; + if (!raw) throw new Error("BILLING_DATA_ENCRYPTION_KEY_missing"); + const decoded = Buffer.from(raw, "base64"); + if (decoded.length !== 32) { + throw new Error("BILLING_DATA_ENCRYPTION_KEY_must_be_32_bytes"); + } + return decoded; +} + +export function encryptBillingValue(value: string): string { + const iv = crypto.randomBytes(12); + const cipher = crypto.createCipheriv(algorithm, key(), iv); + const ciphertext = Buffer.concat([ + cipher.update(value, "utf8"), + cipher.final(), + ]); + return [iv, cipher.getAuthTag(), ciphertext] + .map((part) => part.toString("base64")) + .join("."); +} + +export function decryptBillingValue(payload: string): string { + const [iv, tag, ciphertext] = payload.split("."); + if (!iv || !tag || !ciphertext) { + throw new Error("billing_ciphertext_invalid"); + } + const decipher = crypto.createDecipheriv( + algorithm, + key(), + Buffer.from(iv, "base64"), + ); + decipher.setAuthTag(Buffer.from(tag, "base64")); + return Buffer.concat([ + decipher.update(Buffer.from(ciphertext, "base64")), + decipher.final(), + ]).toString("utf8"); +} diff --git a/apps/api/src/billing/dodo.ts b/apps/api/src/billing/dodo.ts new file mode 100644 index 00000000..b5cbc0d9 --- /dev/null +++ b/apps/api/src/billing/dodo.ts @@ -0,0 +1,31 @@ +import { systemClock, type Clock } from "@codelitdev/billing/core"; +import { createDodoBillingProvider } from "@codelitdev/billing/providers/dodo"; +import type { DodoBillingProviderOptions } from "@codelitdev/billing/providers"; + +export function dodoOptionsFromEnv( + env: NodeJS.ProcessEnv = process.env, + clock: Clock = systemClock, +): DodoBillingProviderOptions { + const environment = env.DODO_PAYMENTS_ENVIRONMENT; + return { + apiKey: env.DODO_PAYMENTS_API_KEY?.trim() ?? "", + environment: + environment === "live_mode" || environment === "test_mode" + ? environment + : "test_mode", + webhookSecrets: [ + { + version: "current", + secret: env.DODO_PAYMENTS_WEBHOOK_KEY_CURRENT?.trim() ?? "", + }, + ], + clock, + }; +} + +export function createMedialitDodoProvider( + env: NodeJS.ProcessEnv = process.env, + clock: Clock = systemClock, +) { + return createDodoBillingProvider(dodoOptionsFromEnv(env, clock)); +} diff --git a/apps/api/src/billing/engine.ts b/apps/api/src/billing/engine.ts new file mode 100644 index 00000000..28e61020 --- /dev/null +++ b/apps/api/src/billing/engine.ts @@ -0,0 +1,109 @@ +import { systemClock } from "@codelitdev/billing/core"; +import { createDrizzleBillingStore } from "@codelitdev/billing/drizzle"; +import { + createBilling, + type BillingEngine, +} from "@codelitdev/billing/workflows"; +import { getDb } from "@/db"; +import * as billingSchema from "@/db/schema/billing.generated"; +import logger from "../services/log"; +import { medialitBillingAuthorization } from "./authorization"; +import { + billingCatalogKeys, + billingComposition, + type BillingComposition, +} from "./catalog"; +import { decryptBillingValue, encryptBillingValue } from "./crypto"; +import { createMedialitDodoProvider } from "./dodo"; +import { applyMedialitProjectionEffects } from "./product-effects"; + +const clock = systemClock; + +function returnUrlAllowed(url: string): boolean { + const webOrigin = process.env.WEB_ORIGIN || process.env.WEB_CLIENT; + if (!webOrigin) return false; + try { + return new URL(url).origin === new URL(webOrigin).origin; + } catch { + return false; + } +} + +let engine: BillingEngine | undefined; + +export function resetBillingEngine(): void { + engine = undefined; +} + +export function getBillingEngine(): BillingEngine { + if (engine) return engine; + const composition = billingComposition(); + const cloud = composition.deploymentMode === "cloud"; + const store = createDrizzleBillingStore(getDb() as never, { + schema: billingSchema, + clock, + }); + engine = createBilling({ + database: store, + providers: cloud ? [createMedialitDodoProvider()] : [], + clock, + authorization: medialitBillingAuthorization, + sensitiveValues: cloud + ? { + async encrypt(plaintext: string) { + return { + ciphertext: encryptBillingValue(plaintext), + keyVersion: "v1", + }; + }, + async decrypt(ciphertext: string, _context) { + return decryptBillingValue(ciphertext); + }, + } + : undefined, + hooks: cloud + ? { + audit: { + async record(event) { + logger.info( + { + effectId: event.effectId, + actor: event.actor.kind, + }, + "billing audit", + ); + }, + }, + lifecycle: { + afterProjection: (input) => + applyMedialitProjectionEffects( + input, + (store.getTransaction() ?? getDb()) as never, + ), + }, + } + : undefined, + ...billingEngineCheckout(composition), + returnUrlValidator: cloud ? returnUrlAllowed : undefined, + }); + return engine; +} + +export function billingEngineCheckout(composition: BillingComposition) { + if (composition.deploymentMode === "oss") { + return { + mode: "oss" as const, + checkoutProvider: "", + requestedRevision: null, + requiredOfferKeys: [] as string[], + offers: [], + }; + } + return { + mode: "cloud" as const, + checkoutProvider: "dodo", + requestedRevision: composition.catalogRevision, + requiredOfferKeys: [...billingCatalogKeys], + offers: composition.offers, + }; +} diff --git a/apps/api/src/billing/entitlements.ts b/apps/api/src/billing/entitlements.ts new file mode 100644 index 00000000..59729bd1 --- /dev/null +++ b/apps/api/src/billing/entitlements.ts @@ -0,0 +1,42 @@ +import { getSubscriptionStatus, type User } from "@medialit/models"; +import { + maxFileUploadSizeNotSubscribed, + maxFileUploadSizeSubscribed, + maxStorageAllowedNotSubscribed, + maxStorageAllowedSubscribed, +} from "../config/constants"; +import { deploymentMode, type BillingDeploymentMode } from "./catalog"; + +export const UNLIMITED_BYTES = Number.MAX_SAFE_INTEGER; + +export type AccountPlan = "oss" | "basic" | "pro"; + +type PlanUser = Pick; + +export function accountPlan( + user: PlanUser, + mode: BillingDeploymentMode = deploymentMode(), +): AccountPlan { + if (mode === "oss") return "oss"; + return getSubscriptionStatus(user as User) ? "pro" : "basic"; +} + +export function maxStorageFor( + user: PlanUser, + mode: BillingDeploymentMode = deploymentMode(), +): number { + if (mode === "oss") return UNLIMITED_BYTES; + return getSubscriptionStatus(user as User) + ? maxStorageAllowedSubscribed + : maxStorageAllowedNotSubscribed; +} + +export function maxUploadFor( + user: PlanUser, + mode: BillingDeploymentMode = deploymentMode(), +): number { + if (mode === "oss") return UNLIMITED_BYTES; + return getSubscriptionStatus(user as User) + ? maxFileUploadSizeSubscribed + : maxFileUploadSizeNotSubscribed; +} diff --git a/apps/api/src/billing/maintenance.ts b/apps/api/src/billing/maintenance.ts new file mode 100644 index 00000000..6adceb38 --- /dev/null +++ b/apps/api/src/billing/maintenance.ts @@ -0,0 +1,51 @@ +import logger from "../services/log"; +import { getBillingEngine } from "./engine"; + +const HOUR_MS = 60 * 60 * 1000; + +/** Starts the billing timers and returns a function that stops them. */ +export function startBillingMaintenance(): () => void { + const billing = getBillingEngine(); + const workerId = `billing-${process.pid}`; + void billing.recordRequestedCatalog().catch((error) => { + logger.error({ err: error }, "Failed to record billing catalog"); + }); + const batches = setInterval(() => { + void billing + .verifyRequestedCatalog() + .catch((error) => + logger.error( + { err: error }, + "Billing catalog verification failed", + ), + ); + void billing + .runWebhookInboxBatch({ workerId }) + .catch((error) => + logger.error({ err: error }, "Billing webhook batch failed"), + ); + void billing + .runReconciliationBatch({ workerId: `${workerId}-reconcile` }) + .catch((error) => + logger.error({ err: error }, "Billing reconciliation failed"), + ); + void billing + .runDeadlineBatch() + .catch((error) => + logger.error({ err: error }, "Billing deadline batch failed"), + ); + }, 60 * 1000); + const retention = setInterval(() => { + void billing + .purgeExpiredSensitiveValues({ + before: new Date(Date.now() - 30 * 24 * HOUR_MS), + }) + .catch((error) => + logger.error({ err: error }, "Billing retention failed"), + ); + }, HOUR_MS); + return () => { + clearInterval(batches); + clearInterval(retention); + }; +} diff --git a/apps/api/src/billing/plans.test.ts b/apps/api/src/billing/plans.test.ts new file mode 100644 index 00000000..c4bb624d --- /dev/null +++ b/apps/api/src/billing/plans.test.ts @@ -0,0 +1,182 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { BillingConfigurationError } from "@codelitdev/billing/core"; +import { Constants } from "@medialit/models"; +import { + maxFileUploadSizeNotSubscribed, + maxFileUploadSizeSubscribed, + maxStorageAllowedNotSubscribed, + maxStorageAllowedSubscribed, +} from "../config/constants"; +import { billingComposition, readCloudBillingConfig } from "./catalog"; +import { billingEngineCheckout } from "./engine"; +import { createBilling } from "@codelitdev/billing/workflows"; +import { systemClock } from "@codelitdev/billing/core"; +import { medialitBillingAuthorization } from "./authorization"; +import { + UNLIMITED_BYTES, + accountPlan, + maxStorageFor, + maxUploadFor, +} from "./entitlements"; +import { profilePatchFromSubscription } from "./product-effects"; +import type { CanonicalSubscription } from "@codelitdev/billing/core"; + +const basic = { + subscriptionStatus: Constants.SubscriptionStatus.NOT_SUBSCRIBED, + subscriptionEndsAfter: undefined, +}; +const pro = { + subscriptionStatus: Constants.SubscriptionStatus.SUBSCRIBED, + subscriptionEndsAfter: undefined, +}; + +test("cloud keeps the current basic and pro limits", () => { + assert.equal(accountPlan(basic, "cloud"), "basic"); + assert.equal(accountPlan(pro, "cloud"), "pro"); + assert.equal(maxStorageFor(basic, "cloud"), maxStorageAllowedNotSubscribed); + assert.equal(maxStorageFor(pro, "cloud"), maxStorageAllowedSubscribed); + assert.equal(maxUploadFor(basic, "cloud"), maxFileUploadSizeNotSubscribed); + assert.equal(maxUploadFor(pro, "cloud"), maxFileUploadSizeSubscribed); +}); + +test("oss unlocks storage and upload size", () => { + assert.equal(accountPlan(basic, "oss"), "oss"); + assert.equal(maxStorageFor(basic, "oss"), UNLIMITED_BYTES); + assert.equal(maxUploadFor(pro, "oss"), UNLIMITED_BYTES); +}); + +test("cloud billing config stays off until Dodo is fully set", () => { + assert.equal( + readCloudBillingConfig({ MEDIALIT_DEPLOYMENT_MODE: "cloud" }), + null, + ); + assert.equal( + billingComposition({ MEDIALIT_DEPLOYMENT_MODE: "cloud" }) + .deploymentMode, + "oss", + ); + assert.equal(billingComposition({}).deploymentMode, "oss"); + assert.equal( + readCloudBillingConfig({ + MEDIALIT_DEPLOYMENT_MODE: "oss", + DODO_PAYMENTS_API_KEY: "test", + }), + null, + ); + const ossCheckout = billingEngineCheckout({ deploymentMode: "oss" }); + assert.equal(ossCheckout.mode, "oss"); + assert.equal(ossCheckout.checkoutProvider, ""); + assert.equal(ossCheckout.requestedRevision, null); + assert.deepEqual(ossCheckout.requiredOfferKeys, []); + assert.deepEqual(ossCheckout.offers, []); + assert.doesNotThrow(() => + createBilling({ + clock: systemClock, + authorization: medialitBillingAuthorization, + providers: [], + ...ossCheckout, + }), + ); + assert.throws( + () => + readCloudBillingConfig({ + MEDIALIT_DEPLOYMENT_MODE: "cloud", + DODO_PAYMENTS_API_KEY: "test", + }), + BillingConfigurationError, + ); +}); + +test("a complete Pro catalog is ten dollars a month or one hundred a year", () => { + const config = readCloudBillingConfig({ + MEDIALIT_DEPLOYMENT_MODE: "cloud", + BILLING_CATALOG_REVISION: "1", + BILLING_CURRENCY: "usd", + BILLING_PRO_MONTH_AMOUNT_MINOR: "1000", + BILLING_PRO_YEAR_AMOUNT_MINOR: "10000", + DODO_PRO_MONTH_PRODUCT_ID: "pdt_pro_month", + DODO_PRO_YEAR_PRODUCT_ID: "pdt_pro_year", + DODO_PAYMENTS_API_KEY: "test_key", + DODO_PAYMENTS_WEBHOOK_KEY_CURRENT: "whsec_test", + BILLING_DATA_ENCRYPTION_KEY: Buffer.alloc(32, 1).toString("base64"), + }); + const month = config?.offers.find((offer) => offer.key === "pro_month"); + const year = config?.offers.find((offer) => offer.key === "pro_year"); + assert.equal(month?.plan, "pro"); + assert.equal(month?.interval, "month"); + assert.equal(month?.amountMinor, 1000); + assert.equal(month?.currency, "USD"); + assert.equal(month?.provider, "dodo"); + assert.equal(year?.interval, "year"); + assert.equal(year?.amountMinor, 10000); + assert.equal(year?.providerProductId, "pdt_pro_year"); + assert.throws( + () => + readCloudBillingConfig({ + MEDIALIT_DEPLOYMENT_MODE: "cloud", + BILLING_CATALOG_REVISION: "1", + BILLING_CURRENCY: "USD", + BILLING_PRO_MONTH_AMOUNT_MINOR: "1000", + BILLING_PRO_YEAR_AMOUNT_MINOR: "10000", + DODO_PRO_MONTH_PRODUCT_ID: "pdt_same", + DODO_PRO_YEAR_PRODUCT_ID: "pdt_same", + DODO_PAYMENTS_API_KEY: "test_key", + DODO_PAYMENTS_WEBHOOK_KEY_CURRENT: "whsec_test", + BILLING_DATA_ENCRYPTION_KEY: Buffer.alloc(32, 1).toString( + "base64", + ), + }), + BillingConfigurationError, + ); +}); + +function subscription( + patch: Partial, +): CanonicalSubscription { + return { + id: "sub_row", + billableEntityId: "user_1", + payerId: "user_1", + provider: "dodo", + providerCustomerId: "cus_1", + providerSubscriptionId: "sub_1", + providerProductId: "pdt_pro_month", + catalogRevision: 1, + offerKey: "pro_month", + plan: "pro", + interval: "month", + priceEntryId: "price_1", + status: "active", + currentPeriodStartsAt: null, + currentPeriodEndsAt: new Date("2027-07-04T10:13:39.000Z"), + paidThroughAt: null, + trialEndsAt: null, + cancelAtPeriodEnd: false, + isEntitlementSource: true, + originCheckoutAttemptId: null, + providerOccurredAt: null, + providerVersion: null, + lastObservedAt: null, + lastReconciledAt: null, + ...patch, + }; +} + +test("an entitled Dodo subscription projects to Pro", () => { + const patch = profilePatchFromSubscription(subscription({})); + assert.equal(patch.subscriptionStatus, "subscribed"); + assert.equal(patch.subscriptionMethod, "dodo"); + assert.equal(patch.subscriptionId, "sub_1"); + assert.equal( + patch.subscriptionEndsAfter?.toISOString(), + "2027-07-04T10:13:39.000Z", + ); +}); + +test("a Dodo subscription that is not the entitlement source drops to Basic", () => { + const patch = profilePatchFromSubscription( + subscription({ isEntitlementSource: false, status: "expired" }), + ); + assert.equal(patch.subscriptionStatus, "not-subscribed"); +}); diff --git a/apps/api/src/billing/product-effects.ts b/apps/api/src/billing/product-effects.ts new file mode 100644 index 00000000..741f55b3 --- /dev/null +++ b/apps/api/src/billing/product-effects.ts @@ -0,0 +1,63 @@ +import { eq } from "drizzle-orm"; +import type { CanonicalSubscription } from "@codelitdev/billing/core"; +import type { SubscriptionStatus } from "@medialit/models"; +import { profiles } from "@/db/schema/domain"; + +type ProfileWriter = { + update: (table: typeof profiles) => { + set: (values: { + subscriptionMethod: "dodo"; + customerId: string; + subscriptionId: string; + subscriptionStatus: SubscriptionStatus; + subscriptionEndsAfter: Date | null; + }) => { + where: (clause: unknown) => Promise; + }; + }; +}; + +export function profilePatchFromSubscription(next: CanonicalSubscription): { + subscriptionMethod: "dodo"; + customerId: string; + subscriptionId: string; + subscriptionStatus: SubscriptionStatus; + subscriptionEndsAfter: Date | null; +} { + const ends = next.paidThroughAt ?? next.currentPeriodEndsAt ?? null; + if (!next.isEntitlementSource) { + return { + subscriptionMethod: "dodo", + customerId: next.providerCustomerId, + subscriptionId: next.providerSubscriptionId, + subscriptionStatus: "not-subscribed", + subscriptionEndsAfter: ends, + }; + } + let subscriptionStatus: SubscriptionStatus = "subscribed"; + if (next.status === "past_due") subscriptionStatus = "paused"; + else if (next.status === "cancelled") subscriptionStatus = "cancelled"; + else if (next.status === "expired") subscriptionStatus = "expired"; + return { + subscriptionMethod: "dodo", + customerId: next.providerCustomerId, + subscriptionId: next.providerSubscriptionId, + subscriptionStatus, + subscriptionEndsAfter: ends, + }; +} + +export async function applyMedialitProjectionEffects( + input: { + material: boolean; + next: CanonicalSubscription; + }, + tx: ProfileWriter, +): Promise { + if (!input.material) return; + const patch = profilePatchFromSubscription(input.next); + await tx + .update(profiles) + .set(patch) + .where(eq(profiles.userId, input.next.billableEntityId)); +} diff --git a/apps/api/src/billing/routes.ts b/apps/api/src/billing/routes.ts new file mode 100644 index 00000000..085981b9 --- /dev/null +++ b/apps/api/src/billing/routes.ts @@ -0,0 +1,260 @@ +import { Router, type Request, type Response } from "express"; +import { desc, eq } from "drizzle-orm"; +import { fromNodeHeaders } from "better-auth/node"; +import { + BillingConfigurationError, + BillingWorkflowError, +} from "@codelitdev/billing/core"; +import { findUserById, getDb, type AccountUser } from "@/db"; +import { billingSubscriptions } from "@/db/schema/billing.generated"; +import type { MedialitAuth } from "../auth/better-auth"; +import logger from "../services/log"; +import { preconsumedGrant } from "./authorization"; +import { + billingComposition, + deploymentMode, + proOfferKey, + type BillingInterval, +} from "./catalog"; +import { getBillingEngine } from "./engine"; + +function webOrigin(): string { + return ( + process.env.WEB_ORIGIN || + process.env.WEB_CLIENT || + "http://localhost:3000" + ).replace(/\/$/, ""); +} + +function headerMap(req: Request): Record { + const headers: Record = {}; + for (const [key, value] of Object.entries(req.headers)) { + if (value == null) continue; + headers[key.toLowerCase()] = Array.isArray(value) + ? value.join(",") + : value; + } + return headers; +} + +async function sessionUser( + auth: MedialitAuth, + req: Request, + res: Response, +): Promise { + const session = await auth.auth.api.getSession({ + headers: fromNodeHeaders(req.headers), + }); + if (!session?.user?.id) { + res.status(401).json({ error: "Unauthenticated" }); + return null; + } + const user = await findUserById(session.user.id); + if (!user) { + res.status(401).json({ error: "Unauthenticated" }); + return null; + } + return user; +} + +function sendBillingError(res: Response, error: unknown) { + if (error instanceof BillingWorkflowError) { + res.status(409).json({ error: error.code }); + return; + } + if (error instanceof BillingConfigurationError) { + res.status(503).json({ error: "Billing is not configured" }); + return; + } + logger.error({ err: error }, "Billing request failed"); + res.status(502).json({ error: "Billing request failed" }); +} + +function payer(user: AccountUser) { + return { id: user.id, email: user.email, name: user.name }; +} + +function readInterval(body: unknown): BillingInterval | "missing" | "invalid" { + if (!body || typeof body !== "object" || !("interval" in body)) { + return "missing"; + } + const interval = (body as { interval?: unknown }).interval; + if (interval === undefined || interval === null || interval === "") { + return "missing"; + } + if (interval === "month" || interval === "year") return interval; + return "invalid"; +} + +async function storedDodoInterval(userId: string): Promise { + const rows = await getDb() + .select({ interval: billingSubscriptions.billingInterval }) + .from(billingSubscriptions) + .where(eq(billingSubscriptions.billableEntityId, userId)) + .orderBy(desc(billingSubscriptions.updatedAt)) + .limit(1); + return rows[0]?.interval === "year" ? "year" : "month"; +} + +function configuredBilling() { + if (billingComposition().deploymentMode !== "cloud") return null; + return getBillingEngine(); +} + +async function beginProCheckout( + res: Response, + user: AccountUser, + interval: BillingInterval, +) { + const billing = configuredBilling(); + if (!billing) { + res.status(503).json({ error: "Billing is not configured" }); + return; + } + const catalog = await billing.publicCatalog(); + if (!catalog?.checkoutAvailable) { + res.status(409).json({ error: "checkout_unavailable" }); + return; + } + const { checkoutUrl } = await billing.startCheckout({ + grant: preconsumedGrant("checkout", user.id), + entity: { kind: "user", id: user.id }, + payer: payer(user), + offerKey: proOfferKey(interval), + catalogRevision: catalog.revision, + returnUrl: `${webOrigin()}/account/billing`, + trialDays: 0, + }); + res.json({ checkoutUrl }); +} + +export function createBillingRouter(auth: MedialitAuth) { + const router = Router(); + + router.post("/api/account/billing/checkout", async (req, res) => { + const user = await sessionUser(auth, req, res); + if (!user) return; + if (deploymentMode() === "oss") { + res.status(404).json({ error: "Billing is not used in OSS mode" }); + return; + } + const interval = readInterval(req.body); + if (interval === "missing" || interval === "invalid") { + res.status(400).json({ error: "Choose monthly or yearly" }); + return; + } + try { + await beginProCheckout(res, user, interval); + } catch (error) { + sendBillingError(res, error); + } + }); + + router.post("/api/account/billing/portal", async (req, res) => { + const user = await sessionUser(auth, req, res); + if (!user) return; + const billing = configuredBilling(); + if (!billing) { + res.status(503).json({ error: "Billing is not configured" }); + return; + } + try { + const portal = await billing.startPortal({ + grant: preconsumedGrant("portal", user.id), + entity: { kind: "user", id: user.id }, + payer: payer(user), + returnUrl: `${webOrigin()}/account/billing`, + }); + res.json({ portalUrl: portal.portalUrl }); + } catch (error) { + sendBillingError(res, error); + } + }); + + router.post("/api/account/billing/cancel", async (req, res) => { + const user = await sessionUser(auth, req, res); + if (!user) return; + if (user.subscriptionMethod !== "dodo") { + res.status(400).json({ error: "No subscription" }); + return; + } + const billing = configuredBilling(); + if (!billing) { + res.status(503).json({ error: "Billing is not configured" }); + return; + } + try { + await billing.cancel({ + grant: preconsumedGrant("cancellation", user.id), + entity: { kind: "user", id: user.id }, + payer: payer(user), + }); + res.json({ success: true }); + } catch (error) { + sendBillingError(res, error); + } + }); + + router.post("/api/account/billing/resume", async (req, res) => { + const user = await sessionUser(auth, req, res); + if (!user) return; + if (user.subscriptionMethod !== "dodo") { + res.status(400).json({ error: "No subscription" }); + return; + } + const requested = readInterval(req.body); + if (requested === "invalid") { + res.status(400).json({ error: "Choose monthly or yearly" }); + return; + } + try { + const interval = + requested === "missing" + ? await storedDodoInterval(user.id) + : requested; + await beginProCheckout(res, user, interval); + } catch (error) { + sendBillingError(res, error); + } + }); + + return router; +} + +export function dodoWebhookRouter() { + const router = Router(); + router.post("/", async (req, res) => { + const billing = configuredBilling(); + if (!billing) { + res.status(503).json({ error: "Billing is not configured" }); + return; + } + const rawBody = (req as Request & { rawBody?: Buffer }).rawBody; + if (!rawBody) { + res.status(400).json({ accepted: false }); + return; + } + try { + const ingested = await billing.ingestWebhook({ + provider: "dodo", + raw: { + body: rawBody.toString("utf8"), + headers: headerMap(req), + }, + }); + res.status(ingested.duplicate ? 200 : 202).json({ accepted: true }); + void billing + .runWebhookInboxBatch({ workerId: `billing-${process.pid}` }) + .catch((error) => { + logger.error( + { err: error }, + "Dodo webhook projection failed", + ); + }); + } catch (error) { + logger.warn({ err: error }, "Dodo webhook rejected"); + res.status(400).json({ accepted: false }); + } + }); + return router; +} diff --git a/apps/api/src/config/constants.ts b/apps/api/src/config/constants.ts index f520c670..32b30a18 100644 --- a/apps/api/src/config/constants.ts +++ b/apps/api/src/config/constants.ts @@ -1,7 +1,5 @@ // App config export const appName = process.env.APP_NAME || "MediaLit"; -export const jwtSecret = process.env.JWT_SECRET || "r@nd0m1e"; -export const jwtExpire = process.env.JWT_EXPIRES_IN || "1d"; export const tempFileDirForUploads = process.env.TEMP_FILE_DIR_FOR_UPLOADS; export const maxFileUploadSizeSubscribed = process.env .MAX_UPLOAD_SIZE_SUBSCRIBED @@ -34,9 +32,6 @@ export const thumbnailWidth = 120; export const thumbnailHeight = 69; export const numberOfRecordsPerPage = 10; -// Database config -export const dbConnectionString = process.env.DB_CONNECTION_STRING; - // Mail config export const mailHost = process.env.EMAIL_HOST; export const mailUser = process.env.EMAIL_USER; diff --git a/apps/api/src/config/db.ts b/apps/api/src/config/db.ts index 3c948918..f21fd272 100644 --- a/apps/api/src/config/db.ts +++ b/apps/api/src/config/db.ts @@ -1,44 +1,16 @@ -import mongoose from "mongoose"; +import pg from "pg"; import logger from "../services/log"; -import { dbConnectionString } from "./constants"; -interface ConnectionProps { - isConnected?: number; -} - -const connection: ConnectionProps = {}; - -export default async function (): Promise { - if (!dbConnectionString) { - logger.error("DB_CONNECTION_STRING is not defined"); - process.exit(1); - } - - if (connection.isConnected) { - return; +export async function checkDatabaseConnection(): Promise { + const connectionString = process.env.DATABASE_URL; + if (!connectionString) { + throw new Error("DATABASE_URL is not set"); } - - const options: mongoose.ConnectOptions = { - useNewUrlParser: true, - useUnifiedTopology: true, - serverSelectionTimeoutMS: 5000, - } as mongoose.ConnectOptions; - + const pool = new pg.Pool({ connectionString }); try { - const dbConnection = await mongoose.connect( - dbConnectionString, - options, - ); - connection.isConnected = dbConnection.connections[0].readyState; + await pool.query("select 1"); logger.info("Database connected"); - } catch (err) { - if (err instanceof Error) { - logger.error({ err }, err.message); - } - process.exit(1); + } finally { + await pool.end(); } } - -export async function disconnect(): Promise { - return await mongoose.disconnect(); -} diff --git a/apps/api/src/dashboard/routes.ts b/apps/api/src/dashboard/routes.ts new file mode 100644 index 00000000..513519e1 --- /dev/null +++ b/apps/api/src/dashboard/routes.ts @@ -0,0 +1,220 @@ +import { + Router, + type NextFunction, + type Request, + type Response, +} from "express"; +import { fromNodeHeaders } from "better-auth/node"; +import { + createApiKey, + findUserById, + getApiKeyByKeyId, + listApiKeys, + renameApiKey, + softDeleteApiKey, + type AccountUser, + type ApiKeyRecord, +} from "@/db"; +import type { MedialitAuth } from "../auth/better-auth"; +import { accountPlan, maxStorageFor } from "../billing/entitlements"; +import { deploymentMode } from "../billing/catalog"; +import mediaService from "../media/service"; +import { getMediaCount, getTotalSpace } from "../media/queries"; + +function publicApp(row: ApiKeyRecord, includeSecret: boolean) { + return { + keyId: row.keyId, + name: row.name, + default: row.default, + ...(includeSecret ? { key: row.key } : {}), + }; +} + +async function sessionUser( + auth: MedialitAuth, + req: Request, + res: Response, +): Promise { + const session = await auth.auth.api.getSession({ + headers: fromNodeHeaders(req.headers), + }); + if (!session?.user?.id) { + res.status(401).json({ error: "Unauthenticated" }); + return null; + } + const user = await findUserById(session.user.id); + if (!user) { + res.status(401).json({ error: "Unauthenticated" }); + return null; + } + return user; +} + +async function ownedApp(auth: MedialitAuth, req: Request, res: Response) { + const user = await sessionUser(auth, req, res); + if (!user) return null; + const key = await getApiKeyByKeyId(user.id, String(req.params.keyId)); + if (!key) { + res.status(404).json({ error: "Apikey not found" }); + return null; + } + return { user, key }; +} + +function catchAsync( + handler: (req: Request, res: Response) => Promise, +) { + return (req: Request, res: Response, next: NextFunction) => { + Promise.resolve() + .then(() => handler(req, res)) + .catch(next); + }; +} + +export function createDashboardRouter(auth: MedialitAuth) { + const router = Router(); + for (const method of ["get", "post", "patch", "delete"] as const) { + const original = router[method].bind(router); + (router as any)[method] = ( + path: string, + handler: (req: Request, res: Response) => Promise, + ) => original(path, catchAsync(handler)); + } + + router.get("/api/account", async (req, res) => { + const user = await sessionUser(auth, req, res); + if (!user) return; + res.json({ + id: user.id, + email: user.email, + name: user.name, + userId: user.userId, + active: user.active, + subscriptionStatus: user.subscriptionStatus, + subscriptionEndsAfter: user.subscriptionEndsAfter ?? null, + subscriptionMethod: user.subscriptionMethod, + customerId: user.customerId, + subscriptionId: user.subscriptionId, + plan: accountPlan(user), + deploymentMode: deploymentMode(), + }); + }); + + router.get("/api/apps", async (req, res) => { + const user = await sessionUser(auth, req, res); + if (!user) return; + const rows = await listApiKeys(user.id); + res.json(rows.map((row) => publicApp(row, false))); + }); + + router.post("/api/apps", async (req, res) => { + const user = await sessionUser(auth, req, res); + if (!user) return; + const name = String(req.body?.name || "").trim(); + if (!name) { + res.status(400).json({ error: "Name is required" }); + return; + } + try { + const created = await createApiKey({ userId: user.id, name }); + res.status(201).json({ + key: created.key, + keyId: created.keyId, + name: created.name, + }); + } catch (error: any) { + res.status(400).json({ error: error.message }); + } + }); + + router.get("/api/apps/:keyId/media/count", async (req, res) => { + const owned = await ownedApp(auth, req, res); + if (!owned) return; + const count = await getMediaCount({ + userId: owned.user.id, + apikey: owned.key.key, + }); + res.json({ count }); + }); + + router.get("/api/apps/:keyId/stats", async (req, res) => { + const owned = await ownedApp(auth, req, res); + if (!owned) return; + const storage = await getTotalSpace({ + userId: owned.user.id, + apikey: owned.key.key, + }); + res.json({ + storage, + maxStorage: maxStorageFor(owned.user), + }); + }); + + router.get("/api/apps/:keyId/media/:mediaId", async (req, res) => { + const owned = await ownedApp(auth, req, res); + if (!owned) return; + const media = await mediaService.getMediaDetails({ + userId: owned.user.id, + apikey: owned.key.key, + mediaId: String(req.params.mediaId), + }); + if (!media) { + res.status(404).json({ error: "Not found" }); + return; + } + res.json(media); + }); + + router.get("/api/apps/:keyId/media", async (req, res) => { + const owned = await ownedApp(auth, req, res); + if (!owned) return; + const page = Number(req.query.page || 1); + const limit = Number(req.query.limit || 10); + const result = await mediaService.getPage({ + userId: owned.user.id, + apikey: owned.key.key, + page: Number.isFinite(page) ? page : 1, + recordsPerPage: Number.isFinite(limit) ? limit : 10, + }); + res.json(result); + }); + + router.get("/api/apps/:keyId", async (req, res) => { + const owned = await ownedApp(auth, req, res); + if (!owned) return; + res.json(publicApp(owned.key, true)); + }); + + router.patch("/api/apps/:keyId", async (req, res) => { + const owned = await ownedApp(auth, req, res); + if (!owned) return; + const name = String(req.body?.name || "").trim(); + if (!name) { + res.status(400).json({ error: "Name is required" }); + return; + } + try { + await renameApiKey({ + userId: owned.user.id, + keyId: owned.key.keyId, + newName: name, + }); + res.json({ success: true }); + } catch (error: any) { + res.status(400).json({ error: error.message }); + } + }); + + router.delete("/api/apps/:keyId", async (req, res) => { + const owned = await ownedApp(auth, req, res); + if (!owned) return; + try { + await softDeleteApiKey(owned.user.id, owned.key.keyId); + res.json({ success: true }); + } catch (error: any) { + res.status(400).json({ error: error.message }); + } + }); + + return router; +} diff --git a/apps/api/src/db/client.ts b/apps/api/src/db/client.ts new file mode 100644 index 00000000..6db7edc5 --- /dev/null +++ b/apps/api/src/db/client.ts @@ -0,0 +1,31 @@ +import { drizzle, type NodePgDatabase } from "drizzle-orm/node-postgres"; +import pg from "pg"; +import * as schema from "./schema/index.js"; + +export type AppDb = NodePgDatabase; + +let database: AppDb | null = null; +let pool: pg.Pool | null = null; + +export function setDb(next: AppDb): void { + database = next; +} + +export function getDb(): AppDb { + if (database) return database; + const connectionString = process.env.DATABASE_URL; + if (!connectionString) { + throw new Error("DATABASE_URL is not set"); + } + pool = new pg.Pool({ connectionString }); + database = drizzle(pool, { schema }); + return database; +} + +export async function closeDb(): Promise { + if (pool) { + await pool.end(); + pool = null; + } + database = null; +} diff --git a/apps/api/src/db/index.ts b/apps/api/src/db/index.ts new file mode 100644 index 00000000..1ccbb858 --- /dev/null +++ b/apps/api/src/db/index.ts @@ -0,0 +1,3 @@ +export { closeDb, getDb, setDb, type AppDb } from "./client.js"; +export * from "./repository.js"; +export * as schema from "./schema/index.js"; diff --git a/apps/api/src/db/migrate.ts b/apps/api/src/db/migrate.ts new file mode 100644 index 00000000..babb78b9 --- /dev/null +++ b/apps/api/src/db/migrate.ts @@ -0,0 +1,38 @@ +import { existsSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { drizzle } from "drizzle-orm/node-postgres"; +import { migrate } from "drizzle-orm/node-postgres/migrator"; +import pg from "pg"; +import logger from "../services/log"; + +const databaseUrl = process.env.DATABASE_URL; +if (!databaseUrl) { + logger.error("DATABASE_URL is not set"); + process.exit(1); +} + +const here = path.dirname(fileURLToPath(import.meta.url)); +const migrationsFolder = + process.env.MIGRATIONS_FOLDER || + (existsSync(path.resolve("apps/api/drizzle")) + ? path.resolve("apps/api/drizzle") + : path.resolve(here, "../../drizzle")); + +const pool = new pg.Pool({ connectionString: databaseUrl }); +const db = drizzle(pool); + +migrate(db, { migrationsFolder }) + .then(() => { + logger.info({ migrationsFolder }, "Database migrations applied"); + }) + .catch((err) => { + logger.error( + { error: err instanceof Error ? err.message : String(err) }, + "Failed to apply database migrations", + ); + process.exitCode = 1; + }) + .finally(async () => { + await pool.end(); + }); diff --git a/apps/api/src/db/repository.test.ts b/apps/api/src/db/repository.test.ts new file mode 100644 index 00000000..f22291c6 --- /dev/null +++ b/apps/api/src/db/repository.test.ts @@ -0,0 +1,126 @@ +import assert from "node:assert/strict"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { before, describe, test } from "node:test"; +import { PGlite } from "@electric-sql/pglite"; +import { drizzle } from "drizzle-orm/pglite"; +import { migrate } from "drizzle-orm/pglite/migrator"; +import { setDb, type AppDb } from "./client.js"; +import * as schema from "./schema/index.js"; +import { + countMedia, + createAccount, + createApiKey, + createMediaRecord, + createSignature, + deleteExpiredSignatures, + getApiKeyByKeyId, + getApiKeyBySecret, + getMediaRecord, + getSignature, + listMedia, + sealMediaRecord, + softDeleteApiKey, +} from "./repository.js"; + +before(async () => { + const client = new PGlite(); + const db = drizzle(client, { schema }); + await migrate(db, { + migrationsFolder: path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + "../../drizzle", + ), + }); + setDb(db as unknown as AppDb); +}); + +describe("repository", () => { + test("keeps plaintext keys, temp semantics, and group prefixes", async () => { + const user = await createAccount({ + email: "owner@example.com", + name: "Owner", + subscriptionStatus: "subscribed", + }); + const extra = await createApiKey({ + userId: user.id, + name: "Second", + key: "plain-secret", + }); + + await createMediaRecord({ + fileName: "objects/a", + mediaId: "media-sealed", + userId: user.id, + apikey: extra.key, + originalFileName: "a.png", + mimeType: "image/png", + size: 10, + thumbnailGenerated: false, + accessControl: "private", + group: "photos/vacation", + temp: true, + }); + await createMediaRecord({ + fileName: "objects/b", + mediaId: "media-listed", + userId: user.id, + apikey: extra.key, + originalFileName: "b.png", + mimeType: "image/png", + size: 20, + thumbnailGenerated: false, + accessControl: "private", + group: "Photos/other", + temp: false, + }); + + assert.equal( + await countMedia({ userId: user.id, apikey: extra.key }), + 1, + ); + assert.notEqual( + await getMediaRecord({ + userId: user.id, + apikey: extra.key, + mediaId: "media-sealed", + }), + null, + ); + + await sealMediaRecord({ + userId: user.id, + apikey: extra.key, + mediaId: "media-sealed", + }); + assert.equal( + await countMedia({ userId: user.id, apikey: extra.key }), + 2, + ); + + const prefixed = await listMedia({ + userId: user.id, + apikey: extra.key, + group: "photos", + }); + assert.deepEqual( + prefixed.map((row) => row.mediaId), + ["media-sealed"], + ); + + const signature = await createSignature({ + userId: user.id, + apikey: extra.key, + validTill: new Date(Date.now() - 1000), + }); + assert.notEqual(await getSignature(signature.signature), null); + await deleteExpiredSignatures(user.id); + assert.equal(await getSignature(signature.signature), null); + + await softDeleteApiKey(user.id, extra.keyId); + assert.equal(await getApiKeyByKeyId(user.id, extra.keyId), null); + const hidden = await getApiKeyBySecret("plain-secret"); + assert.equal(hidden?.deleted, true); + assert.equal(hidden?.key, "plain-secret"); + }); +}); diff --git a/apps/api/src/db/repository.ts b/apps/api/src/db/repository.ts new file mode 100644 index 00000000..9989bfca --- /dev/null +++ b/apps/api/src/db/repository.ts @@ -0,0 +1,924 @@ +import { randomUUID } from "node:crypto"; +import { getUniqueId } from "@medialit/utils"; +import { and, asc, desc, eq, like, lt, sql } from "drizzle-orm"; +import { getDb } from "./client.js"; +import { + apiKeys, + media, + mediaSettings, + oauthAppSelections, + oauthClient, + profiles, + signatures, + tusUploads, + user, +} from "./schema/index.js"; + +export type SubscriptionStatus = + | "not-subscribed" + | "subscribed" + | "cancelled" + | "paused" + | "expired"; + +const SIGNATURE_LENGTH = 100; +const SIGNATURE_VALIDITY_MINUTES = Number( + process.env.SIGNATURE_VALIDITY_MINUTES || "1440", +); + +export type AccountUser = { + id: string; + _id: string; + userId: string; + email: string; + active: boolean; + name?: string; + customerId?: string; + subscriptionId?: string; + subscriptionEndsAfter?: Date; + subscriptionMethod?: "stripe" | "dodo"; + subscriptionStatus: SubscriptionStatus; +}; + +const API_KEY_RESTRICTIONS = ["referrer", "ipaddress", "custom"] as const; +type ApiKeyRestriction = (typeof API_KEY_RESTRICTIONS)[number]; + +export type ApiKeyRecord = { + id: string; + keyId: string; + name: string; + key: string; + userId: string; + restriction?: ApiKeyRestriction; + httpReferrers: string[]; + ipAddresses: string[]; + default: boolean; + deleted: boolean; + createdAt: Date; + updatedAt: Date; +}; + +export type MediaRecord = { + id: string; + _id: string; + fileName: string; + mediaId: string; + userId: string; + apikey: string; + originalFileName: string; + mimeType: string; + size: number; + thumbnailGenerated: boolean; + accessControl: "public" | "private"; + group?: string; + caption?: string; + temp: boolean; + createdAt: Date; + updatedAt: Date; +}; + +export type MediaSettingsRecord = { + userId: string; + apikey: string; + useWebP?: boolean; + webpOutputQuality?: number; + thumbnailWidth?: number; + thumbnailHeight?: number; +}; + +export type SignatureRecord = { + id: string; + userId: string; + apikey: string; + signature: string; + validTill: Date; + group?: string; +}; + +export type TusMetadata = { + fileName: string; + mimeType: string; + accessControl: string; + caption?: string; + group?: string; +}; + +export type TusUploadRecord = { + id: string; + _id: string; + uploadId: string; + userId: string; + apikey: string; + uploadLength: number; + uploadOffset: number; + metadata: TusMetadata; + tempFilePath?: string; + signature?: string; + isComplete: boolean; + expiresAt?: Date; + createdAt: Date; +}; + +function now() { + return new Date(); +} + +function mapUser( + account: typeof user.$inferSelect, + profile: typeof profiles.$inferSelect, +): AccountUser { + return { + id: account.id, + _id: account.id, + userId: profile.publicUserId, + email: account.email, + active: profile.active, + name: account.name || undefined, + customerId: profile.customerId || undefined, + subscriptionId: profile.subscriptionId || undefined, + subscriptionEndsAfter: profile.subscriptionEndsAfter || undefined, + subscriptionMethod: + (profile.subscriptionMethod as "stripe" | "dodo" | null) || + undefined, + subscriptionStatus: profile.subscriptionStatus as SubscriptionStatus, + }; +} + +function mapKey(row: typeof apiKeys.$inferSelect): ApiKeyRecord { + return { + id: row.id, + keyId: row.keyId, + name: row.name, + key: row.key, + userId: row.userId, + restriction: API_KEY_RESTRICTIONS.includes( + row.restriction as ApiKeyRestriction, + ) + ? (row.restriction as ApiKeyRestriction) + : undefined, + httpReferrers: row.httpReferrers ?? [], + ipAddresses: row.ipAddresses ?? [], + default: row.isDefault, + deleted: row.deleted, + createdAt: row.createdAt, + updatedAt: row.updatedAt, + }; +} + +function mapMedia(row: typeof media.$inferSelect): MediaRecord { + return { + id: row.id, + _id: row.id, + fileName: row.fileName, + mediaId: row.mediaId, + userId: row.userId, + apikey: row.apikey, + originalFileName: row.originalFileName, + mimeType: row.mimeType, + size: row.size, + thumbnailGenerated: row.thumbnailGenerated, + accessControl: row.accessControl === "public" ? "public" : "private", + group: row.group || undefined, + caption: row.caption || undefined, + temp: row.temp, + createdAt: row.createdAt, + updatedAt: row.updatedAt, + }; +} + +export async function findUserById(id: string): Promise { + const db = getDb(); + const rows = await db + .select({ account: user, profile: profiles }) + .from(user) + .innerJoin(profiles, eq(profiles.userId, user.id)) + .where(eq(user.id, id)) + .limit(1); + const byPublic = rows[0] + ? rows + : await db + .select({ account: user, profile: profiles }) + .from(user) + .innerJoin(profiles, eq(profiles.userId, user.id)) + .where(eq(profiles.publicUserId, id)) + .limit(1); + const row = byPublic[0]; + return row ? mapUser(row.account, row.profile) : null; +} + +export async function findUserByEmail( + email: string, +): Promise { + const db = getDb(); + const rows = await db + .select({ account: user, profile: profiles }) + .from(user) + .innerJoin(profiles, eq(profiles.userId, user.id)) + .where(eq(user.email, email.toLowerCase())) + .limit(1); + const row = rows[0]; + return row ? mapUser(row.account, row.profile) : null; +} + +export async function findUserByPublicId( + publicUserId: string, +): Promise { + const db = getDb(); + const rows = await db + .select({ account: user, profile: profiles }) + .from(user) + .innerJoin(profiles, eq(profiles.userId, user.id)) + .where(eq(profiles.publicUserId, publicUserId)) + .limit(1); + const row = rows[0]; + return row ? mapUser(row.account, row.profile) : null; +} + +export async function insertAuthUser(input: { + id?: string; + email: string; + name?: string; + emailVerified?: boolean; +}): Promise { + const db = getDb(); + const id = input.id || randomUUID(); + const email = input.email.toLowerCase(); + const timestamp = now(); + await db + .insert(user) + .values({ + id, + email, + name: input.name || email.split("@")[0] || "User", + emailVerified: input.emailVerified ?? true, + createdAt: timestamp, + updatedAt: timestamp, + }) + .onConflictDoNothing({ target: user.email }); + const existing = await db + .select({ id: user.id }) + .from(user) + .where(eq(user.email, email)) + .limit(1); + if (!existing[0]) throw new Error("Failed to create user"); + return existing[0].id; +} + +export async function ensureProfile(input: { + userId: string; + publicUserId?: string; + subscriptionStatus?: SubscriptionStatus; + active?: boolean; +}): Promise { + const db = getDb(); + const existing = await db + .select() + .from(profiles) + .where(eq(profiles.userId, input.userId)) + .limit(1); + if (!existing[0]) { + await db.insert(profiles).values({ + userId: input.userId, + publicUserId: input.publicUserId || getUniqueId(), + active: input.active ?? true, + subscriptionStatus: input.subscriptionStatus || "not-subscribed", + }); + } else if (input.subscriptionStatus) { + await db + .update(profiles) + .set({ subscriptionStatus: input.subscriptionStatus }) + .where(eq(profiles.userId, input.userId)); + } + const keys = await db + .select({ id: apiKeys.id }) + .from(apiKeys) + .where( + and(eq(apiKeys.userId, input.userId), eq(apiKeys.deleted, false)), + ) + .limit(1); + if (!keys[0]) { + await createApiKey({ + userId: input.userId, + name: process.env.DEFAULT_APP_NAME || "My Store", + isDefault: true, + }); + } + const account = await findUserById(input.userId); + if (!account) throw new Error("Failed to load user profile"); + return account; +} + +export async function createAccount(input: { + id?: string; + email: string; + name?: string; + publicUserId?: string; + subscriptionStatus?: SubscriptionStatus; +}): Promise { + const userId = await insertAuthUser(input); + return ensureProfile({ + userId, + publicUserId: input.publicUserId, + subscriptionStatus: input.subscriptionStatus, + }); +} + +export async function updateSubscription( + publicUserId: string, + patch: { + subscriptionMethod?: "stripe" | "dodo"; + customerId?: string; + subscriptionId?: string; + subscriptionStatus?: SubscriptionStatus; + subscriptionEndsAfter?: Date; + }, +): Promise { + const db = getDb(); + await db + .update(profiles) + .set({ + subscriptionMethod: patch.subscriptionMethod, + customerId: patch.customerId, + subscriptionId: patch.subscriptionId, + subscriptionStatus: patch.subscriptionStatus, + subscriptionEndsAfter: patch.subscriptionEndsAfter, + }) + .where(eq(profiles.publicUserId, publicUserId)); +} + +export async function createApiKey(input: { + userId: string; + name: string; + isDefault?: boolean; + key?: string; + keyId?: string; + restriction?: string; + httpReferrers?: string[]; + ipAddresses?: string[]; + deleted?: boolean; +}): Promise { + const db = getDb(); + const timestamp = now(); + const [row] = await db + .insert(apiKeys) + .values({ + id: randomUUID(), + keyId: input.keyId || getUniqueId(), + name: input.name, + key: input.key || getUniqueId(), + userId: input.userId, + restriction: input.restriction, + httpReferrers: input.httpReferrers ?? [], + ipAddresses: input.ipAddresses ?? [], + deleted: input.deleted ?? false, + isDefault: input.isDefault ?? false, + createdAt: timestamp, + updatedAt: timestamp, + }) + .returning(); + if (!row) throw new Error("Failed to create API key"); + return mapKey(row); +} + +export async function getApiKeyBySecret( + key: string, +): Promise { + const db = getDb(); + const [row] = await db + .select() + .from(apiKeys) + .where(eq(apiKeys.key, key)) + .limit(1); + return row ? mapKey(row) : null; +} + +export async function listApiKeys( + userId: string, + keyId?: string, +): Promise { + const db = getDb(); + const filters = [eq(apiKeys.userId, userId), eq(apiKeys.deleted, false)]; + if (keyId) filters.push(eq(apiKeys.keyId, keyId)); + const rows = await db + .select() + .from(apiKeys) + .where(and(...filters)) + .orderBy(asc(apiKeys.createdAt)); + return rows.map(mapKey); +} + +export async function getApiKeyByKeyId( + userId: string, + keyId: string, +): Promise { + const rows = await listApiKeys(userId, keyId); + return rows[0] ?? null; +} + +export async function renameApiKey(input: { + userId: string; + keyId?: string; + name?: string; + newName: string; +}): Promise { + const db = getDb(); + const filters = [ + eq(apiKeys.userId, input.userId), + eq(apiKeys.deleted, false), + ]; + if (input.keyId) filters.push(eq(apiKeys.keyId, input.keyId)); + if (input.name) filters.push(eq(apiKeys.name, input.name)); + await db + .update(apiKeys) + .set({ name: input.newName, updatedAt: now() }) + .where(and(...filters)); +} + +export async function softDeleteApiKey( + userId: string, + keyId: string, +): Promise { + const db = getDb(); + const existing = await getApiKeyByKeyId(userId, keyId); + if (!existing) return; + if (existing.default) { + throw new Error("Default API key cannot be deleted"); + } + await db + .update(apiKeys) + .set({ deleted: true, updatedAt: now() }) + .where(and(eq(apiKeys.userId, userId), eq(apiKeys.keyId, keyId))); +} + +/** Upserts so revisiting the app picker overwrites the earlier choice. */ +export async function setOAuthAppSelection( + sessionId: string, + apiKeyId: string, +): Promise { + const db = getDb(); + await db + .insert(oauthAppSelections) + .values({ sessionId, apiKeyId }) + .onConflictDoUpdate({ + target: oauthAppSelections.sessionId, + set: { apiKeyId, updatedAt: now() }, + }); +} + +/** The app picked for this session, or `null` if the user has not picked one + * or the app has since been deleted. */ +export async function getOAuthAppSelection( + sessionId: string, +): Promise<{ keyId: string; updatedAt: Date } | null> { + const db = getDb(); + const [row] = await db + .select({ + keyId: apiKeys.keyId, + updatedAt: oauthAppSelections.updatedAt, + }) + .from(oauthAppSelections) + .innerJoin(apiKeys, eq(apiKeys.id, oauthAppSelections.apiKeyId)) + .where( + and( + eq(oauthAppSelections.sessionId, sessionId), + eq(apiKeys.deleted, false), + ), + ) + .limit(1); + return row ?? null; +} + +function escapeLike(value: string): string { + return value.replace(/[\\%_]/g, (char) => `\\${char}`); +} + +export async function createMediaRecord( + input: Omit & { + id?: string; + createdAt?: Date; + }, +): Promise { + const db = getDb(); + const timestamp = input.createdAt || now(); + const [row] = await db + .insert(media) + .values({ + id: input.id || randomUUID(), + fileName: input.fileName, + mediaId: input.mediaId, + userId: input.userId, + apikey: input.apikey, + originalFileName: input.originalFileName, + mimeType: input.mimeType, + size: input.size, + thumbnailGenerated: input.thumbnailGenerated, + accessControl: input.accessControl, + group: input.group, + caption: input.caption, + temp: input.temp, + createdAt: timestamp, + updatedAt: input.createdAt || timestamp, + }) + .returning(); + if (!row) throw new Error("Failed to create media"); + return mapMedia(row); +} + +export async function getMediaRecord(input: { + userId: string; + apikey: string; + mediaId: string; +}): Promise { + const db = getDb(); + const [row] = await db + .select() + .from(media) + .where( + and( + eq(media.userId, input.userId), + eq(media.apikey, input.apikey), + eq(media.mediaId, input.mediaId), + ), + ) + .limit(1); + return row ? mapMedia(row) : null; +} + +export async function countMedia(input: { + userId: string; + apikey: string; + access?: "public" | "private"; + group?: string; +}): Promise { + const db = getDb(); + const filters = [ + eq(media.userId, input.userId), + eq(media.apikey, input.apikey), + eq(media.temp, false), + ]; + if (input.access) filters.push(eq(media.accessControl, input.access)); + if (input.group && input.group.trim()) { + filters.push(like(media.group, `${escapeLike(input.group.trim())}%`)); + } + const [row] = await db + .select({ count: sql`count(*)::int` }) + .from(media) + .where(and(...filters)); + return Number(row?.count ?? 0); +} + +export async function listMedia(input: { + userId: string; + apikey: string; + access?: "public" | "private"; + group?: string; + page?: number; + recordsPerPage?: number; +}): Promise { + const db = getDb(); + const limit = input.recordsPerPage || 10; + const offset = input.page ? (input.page - 1) * limit : 0; + const filters = [ + eq(media.userId, input.userId), + eq(media.apikey, input.apikey), + eq(media.temp, false), + ]; + if (input.access) filters.push(eq(media.accessControl, input.access)); + if (input.group && input.group.trim()) { + filters.push(like(media.group, `${escapeLike(input.group.trim())}%`)); + } + const rows = await db + .select() + .from(media) + .where(and(...filters)) + .orderBy(desc(media.createdAt), desc(media.mediaId)) + .limit(limit) + .offset(offset); + return rows.map(mapMedia); +} + +export async function totalMediaSize(input: { + userId: string; + apikey?: string; +}): Promise { + const db = getDb(); + const filters = [eq(media.userId, input.userId), eq(media.temp, false)]; + if (input.apikey) filters.push(eq(media.apikey, input.apikey)); + const [row] = await db + .select({ total: sql`coalesce(sum(${media.size}), 0)::float8` }) + .from(media) + .where(and(...filters)); + return Number(row?.total ?? 0); +} + +export async function deleteMediaRecord( + userId: string, + mediaId: string, +): Promise { + const db = getDb(); + await db + .delete(media) + .where(and(eq(media.userId, userId), eq(media.mediaId, mediaId))); +} + +export async function sealMediaRecord(input: { + userId: string; + apikey: string; + mediaId: string; +}): Promise { + const db = getDb(); + await db + .update(media) + .set({ temp: false, updatedAt: now() }) + .where( + and( + eq(media.userId, input.userId), + eq(media.apikey, input.apikey), + eq(media.mediaId, input.mediaId), + ), + ); +} + +export async function listExpiredTempMedia( + cutoff: Date, +): Promise { + const db = getDb(); + const rows = await db + .select() + .from(media) + .where(and(eq(media.temp, true), lt(media.createdAt, cutoff))); + return rows.map(mapMedia); +} + +export async function deleteMediaById(id: string): Promise { + const db = getDb(); + await db.delete(media).where(eq(media.id, id)); +} + +export async function getMediaSettingsRecord( + userId: string, + apikey: string, +): Promise { + const db = getDb(); + const [row] = await db + .select() + .from(mediaSettings) + .where( + and( + eq(mediaSettings.userId, userId), + eq(mediaSettings.apikey, apikey), + ), + ) + .limit(1); + if (!row) return null; + return { + userId: row.userId, + apikey: row.apikey, + useWebP: row.useWebP ?? undefined, + webpOutputQuality: row.webpOutputQuality ?? undefined, + thumbnailWidth: row.thumbnailWidth ?? undefined, + thumbnailHeight: row.thumbnailHeight ?? undefined, + }; +} + +export async function upsertMediaSettings( + input: MediaSettingsRecord, +): Promise { + const db = getDb(); + await db + .insert(mediaSettings) + .values({ + id: randomUUID(), + userId: input.userId, + apikey: input.apikey, + useWebP: input.useWebP, + webpOutputQuality: input.webpOutputQuality, + thumbnailWidth: input.thumbnailWidth, + thumbnailHeight: input.thumbnailHeight, + }) + .onConflictDoUpdate({ + target: mediaSettings.apikey, + set: { + useWebP: input.useWebP, + webpOutputQuality: input.webpOutputQuality, + thumbnailWidth: input.thumbnailWidth, + thumbnailHeight: input.thumbnailHeight, + }, + }); +} + +export async function createSignature(input: { + userId: string; + apikey: string; + group?: string; + id?: string; + signature?: string; + validTill?: Date; + createdAt?: Date; +}): Promise { + const db = getDb(); + const [row] = await db + .insert(signatures) + .values({ + id: input.id || randomUUID(), + userId: input.userId, + apikey: input.apikey, + signature: input.signature || getUniqueId(SIGNATURE_LENGTH), + validTill: + input.validTill || + new Date(Date.now() + SIGNATURE_VALIDITY_MINUTES * 60000), + group: input.group, + createdAt: input.createdAt || now(), + }) + .returning(); + if (!row) throw new Error("Failed to create signature"); + return { + id: row.id, + userId: row.userId, + apikey: row.apikey, + signature: row.signature, + validTill: row.validTill, + group: row.group || undefined, + }; +} + +export async function getSignature( + signature: string, +): Promise { + const db = getDb(); + const [row] = await db + .select() + .from(signatures) + .where(eq(signatures.signature, signature)) + .limit(1); + if (!row) return null; + return { + id: row.id, + userId: row.userId, + apikey: row.apikey, + signature: row.signature, + validTill: row.validTill, + group: row.group || undefined, + }; +} + +export async function deleteSignatureById(id: string): Promise { + const db = getDb(); + await db.delete(signatures).where(eq(signatures.id, id)); +} + +export async function deleteSignatureValue(signature: string): Promise { + const db = getDb(); + await db.delete(signatures).where(eq(signatures.signature, signature)); +} + +export async function deleteExpiredSignatures(userId: string): Promise { + const db = getDb(); + await db + .delete(signatures) + .where( + and(eq(signatures.userId, userId), lt(signatures.validTill, now())), + ); +} + +export async function createTusUploadRecord(input: { + uploadId: string; + userId: string; + apikey: string; + uploadLength: number; + metadata: TusMetadata; + tempFilePath?: string; + signature?: string; + expiresAt: Date; +}): Promise { + const db = getDb(); + const timestamp = now(); + const [row] = await db + .insert(tusUploads) + .values({ + id: randomUUID(), + uploadId: input.uploadId, + userId: input.userId, + apikey: input.apikey, + uploadLength: input.uploadLength, + uploadOffset: 0, + metadata: input.metadata, + tempFilePath: input.tempFilePath, + signature: input.signature, + isComplete: false, + expiresAt: input.expiresAt, + createdAt: timestamp, + updatedAt: timestamp, + }) + .returning(); + if (!row) throw new Error("Failed to create tus upload"); + return mapTus(row); +} + +function mapTus(row: typeof tusUploads.$inferSelect): TusUploadRecord { + return { + id: row.id, + _id: row.id, + uploadId: row.uploadId, + userId: row.userId, + apikey: row.apikey, + uploadLength: row.uploadLength, + uploadOffset: row.uploadOffset, + metadata: row.metadata as TusMetadata, + tempFilePath: row.tempFilePath || undefined, + signature: row.signature || undefined, + isComplete: row.isComplete, + expiresAt: row.expiresAt || undefined, + createdAt: row.createdAt, + }; +} + +export async function getTusUploadRecord( + uploadId: string, +): Promise { + const db = getDb(); + const [row] = await db + .select() + .from(tusUploads) + .where(eq(tusUploads.uploadId, uploadId)) + .limit(1); + return row ? mapTus(row) : null; +} + +export async function updateTusOffset( + uploadId: string, + uploadOffset: number, +): Promise { + const db = getDb(); + await db + .update(tusUploads) + .set({ uploadOffset, updatedAt: now() }) + .where(eq(tusUploads.uploadId, uploadId)); +} + +export async function markTusComplete(uploadId: string): Promise { + const db = getDb(); + await db + .update(tusUploads) + .set({ isComplete: true, updatedAt: now() }) + .where(eq(tusUploads.uploadId, uploadId)); +} + +export async function deleteTusUploadRecord(uploadId: string): Promise { + const db = getDb(); + await db.delete(tusUploads).where(eq(tusUploads.uploadId, uploadId)); +} + +export async function listTusByUser( + userId: string, +): Promise { + const db = getDb(); + const rows = await db + .select() + .from(tusUploads) + .where(eq(tusUploads.userId, userId)) + .orderBy(desc(tusUploads.createdAt)); + return rows.map(mapTus); +} + +export async function listExpiredTus(before: Date): Promise { + const db = getDb(); + const rows = await db + .select() + .from(tusUploads) + .where(lt(tusUploads.expiresAt, before)); + return rows.map(mapTus); +} + +export async function deleteTusById(id: string): Promise { + const db = getDb(); + await db.delete(tusUploads).where(eq(tusUploads.id, id)); +} + +export async function seedWebOAuthClient(input: { + redirectUris: string[]; +}): Promise { + const db = getDb(); + const existing = await db + .select({ id: oauthClient.id }) + .from(oauthClient) + .where(eq(oauthClient.clientId, "web-client")) + .limit(1); + if (existing[0]) return; + const timestamp = now(); + await db.insert(oauthClient).values({ + id: randomUUID(), + clientId: "web-client", + name: "MediaLit Web", + redirectUris: input.redirectUris, + skipConsent: true, + disabled: false, + tokenEndpointAuthMethod: "none", + grantTypes: ["authorization_code", "refresh_token"], + responseTypes: ["code"], + requirePKCE: true, + scopes: ["openid", "profile", "email", "offline_access"], + createdAt: timestamp, + updatedAt: timestamp, + }); +} diff --git a/apps/api/src/db/schema/auth.generated.ts b/apps/api/src/db/schema/auth.generated.ts new file mode 100644 index 00000000..3027e62c --- /dev/null +++ b/apps/api/src/db/schema/auth.generated.ts @@ -0,0 +1,221 @@ +/** + * AUTO-GENERATED FILE. DO NOT EDIT. + * Generated by Better Auth getSchema (better-auth/db) from the reference + * Better Auth options (jwt + oauth-server-kit oauthProvider). + * Importing this module performs no DDL. + */ +import { + boolean, + integer, + jsonb, + pgTable, + text, + timestamp, +} from "drizzle-orm/pg-core"; + +export const user = pgTable("user", { + id: text("id").primaryKey(), + name: text("name").notNull(), + email: text("email").notNull().unique(), + emailVerified: boolean("email_verified").notNull(), + image: text("image"), + createdAt: timestamp("created_at", { withTimezone: true }).notNull(), + updatedAt: timestamp("updated_at", { withTimezone: true }).notNull(), +}); + +export const session = pgTable("session", { + id: text("id").primaryKey(), + expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(), + token: text("token").notNull().unique(), + createdAt: timestamp("created_at", { withTimezone: true }).notNull(), + updatedAt: timestamp("updated_at", { withTimezone: true }).notNull(), + ipAddress: text("ip_address"), + userAgent: text("user_agent"), + userId: text("user_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), +}); + +export const account = pgTable("account", { + id: text("id").primaryKey(), + accountId: text("account_id").notNull(), + providerId: text("provider_id").notNull(), + userId: text("user_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), + accessToken: text("access_token"), + refreshToken: text("refresh_token"), + idToken: text("id_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 }).notNull(), + updatedAt: timestamp("updated_at", { withTimezone: true }).notNull(), +}); + +export const verification = pgTable("verification", { + 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 }).notNull(), + updatedAt: timestamp("updated_at", { withTimezone: true }).notNull(), +}); + +export const jwks = pgTable("jwks", { + id: text("id").primaryKey(), + publicKey: text("public_key").notNull(), + privateKey: text("private_key").notNull(), + createdAt: timestamp("created_at", { withTimezone: true }).notNull(), + expiresAt: timestamp("expires_at", { withTimezone: true }), + alg: text("alg"), + crv: text("crv"), +}); + +export const oauthClient = pgTable("oauth_client", { + id: text("id").primaryKey(), + clientId: text("client_id").notNull().unique(), + clientSecret: text("client_secret"), + clientDiscoveryId: text("client_discovery_id"), + disabled: boolean("disabled"), + skipConsent: boolean("skip_consent"), + enableEndSession: boolean("enable_end_session"), + subjectType: text("subject_type"), + scopes: text("scopes").array(), + clientCredentialsScopes: text("client_credentials_scopes").array(), + userId: text("user_id").references(() => user.id, { onDelete: "cascade" }), + createdAt: timestamp("created_at", { withTimezone: true }), + updatedAt: timestamp("updated_at", { withTimezone: true }), + name: text("name"), + uri: text("uri"), + icon: text("icon"), + contacts: text("contacts").array(), + tos: text("tos"), + policy: text("policy"), + softwareId: text("software_id"), + softwareVersion: text("software_version"), + softwareStatement: text("software_statement"), + redirectUris: text("redirect_uris").array().notNull(), + postLogoutRedirectUris: text("post_logout_redirect_uris").array(), + backchannelLogoutUri: text("backchannel_logout_uri"), + backchannelLogoutSessionRequired: boolean( + "backchannel_logout_session_required", + ), + tokenEndpointAuthMethod: text("token_endpoint_auth_method"), + applicationType: text("application_type"), + jwks: text("jwks"), + jwksUri: text("jwks_uri"), + grantTypes: text("grant_types").array(), + responseTypes: text("response_types").array(), + requirePKCE: boolean("require_pkce"), + dpopBoundAccessTokens: boolean("dpop_bound_access_tokens"), + referenceId: text("reference_id"), + metadata: jsonb("metadata"), +}); + +export const oauthResource = pgTable("oauth_resource", { + id: text("id").primaryKey(), + identifier: text("identifier").notNull().unique(), + name: text("name").notNull(), + accessTokenTtl: integer("access_token_ttl"), + refreshTokenTtl: integer("refresh_token_ttl"), + signingAlgorithm: text("signing_algorithm"), + signingKeyId: text("signing_key_id"), + allowedScopes: text("allowed_scopes").array(), + customClaims: jsonb("custom_claims"), + dpopBoundAccessTokensRequired: boolean("dpop_bound_access_tokens_required"), + disabled: boolean("disabled"), + createdAt: timestamp("created_at", { withTimezone: true }), + updatedAt: timestamp("updated_at", { withTimezone: true }), + policyVersion: integer("policy_version"), + metadata: jsonb("metadata"), +}); + +export const oauthClientResource = pgTable("oauth_client_resource", { + id: text("id").primaryKey(), + clientId: text("client_id") + .notNull() + .references(() => oauthClient.clientId, { onDelete: "cascade" }), + resourceId: text("resource_id") + .notNull() + .references(() => oauthResource.identifier, { onDelete: "cascade" }), + metadata: jsonb("metadata"), + createdAt: timestamp("created_at", { withTimezone: true }), +}); + +export const oauthRefreshToken = pgTable("oauth_refresh_token", { + id: text("id").primaryKey(), + token: text("token").notNull().unique(), + clientId: text("client_id") + .notNull() + .references(() => oauthClient.clientId, { onDelete: "cascade" }), + sessionId: text("session_id").references(() => session.id, { + onDelete: "set null", + }), + userId: text("user_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), + referenceId: text("reference_id"), + authorizationCodeId: text("authorization_code_id"), + resources: text("resources").array(), + requestedUserInfoClaims: text("requested_user_info_claims").array(), + expiresAt: timestamp("expires_at", { withTimezone: true }), + createdAt: timestamp("created_at", { withTimezone: true }), + revoked: timestamp("revoked", { withTimezone: true }), + rotatedAt: timestamp("rotated_at", { withTimezone: true }), + rotationReplayResponse: text("rotation_replay_response"), + rotationReplayExpiresAt: timestamp("rotation_replay_expires_at", { + withTimezone: true, + }), + authTime: timestamp("auth_time", { withTimezone: true }), + confirmation: jsonb("confirmation"), + scopes: text("scopes").array().notNull(), +}); + +export const oauthAccessToken = pgTable("oauth_access_token", { + id: text("id").primaryKey(), + token: text("token").unique(), + clientId: text("client_id") + .notNull() + .references(() => oauthClient.clientId, { onDelete: "cascade" }), + sessionId: text("session_id").references(() => session.id, { + onDelete: "set null", + }), + userId: text("user_id").references(() => user.id, { onDelete: "cascade" }), + referenceId: text("reference_id"), + authorizationCodeId: text("authorization_code_id"), + resources: text("resources").array(), + requestedUserInfoClaims: text("requested_user_info_claims").array(), + refreshId: text("refresh_id").references(() => oauthRefreshToken.id, { + onDelete: "cascade", + }), + expiresAt: timestamp("expires_at", { withTimezone: true }), + createdAt: timestamp("created_at", { withTimezone: true }), + revoked: timestamp("revoked", { withTimezone: true }), + confirmation: jsonb("confirmation"), + scopes: text("scopes").array().notNull(), +}); + +export const oauthConsent = pgTable("oauth_consent", { + id: text("id").primaryKey(), + clientId: text("client_id") + .notNull() + .references(() => oauthClient.clientId, { onDelete: "cascade" }), + userId: text("user_id").references(() => user.id, { onDelete: "cascade" }), + referenceId: text("reference_id"), + resources: text("resources").array(), + requestedUserInfoClaims: text("requested_user_info_claims").array(), + scopes: text("scopes").array().notNull(), + createdAt: timestamp("created_at", { withTimezone: true }), + updatedAt: timestamp("updated_at", { withTimezone: true }), +}); + +export const oauthClientAssertion = pgTable("oauth_client_assertion", { + id: text("id").primaryKey(), + expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(), +}); diff --git a/apps/api/src/db/schema/billing-payer.ts b/apps/api/src/db/schema/billing-payer.ts new file mode 100644 index 00000000..9ddff1ad --- /dev/null +++ b/apps/api/src/db/schema/billing-payer.ts @@ -0,0 +1 @@ +export { user as payerUser } from "./auth.generated"; diff --git a/apps/api/src/db/schema/billing.generated.ts b/apps/api/src/db/schema/billing.generated.ts new file mode 100644 index 00000000..cfd4b49e --- /dev/null +++ b/apps/api/src/db/schema/billing.generated.ts @@ -0,0 +1,447 @@ +/** + * AUTO-GENERATED FILE. DO NOT EDIT. + * Generated by @codelitdev/billing schema version 2. + */ +import { sql } from "drizzle-orm"; +import { + boolean, + check, + index, + integer, + pgTable, + text, + timestamp, + uniqueIndex, + uuid, +} from "drizzle-orm/pg-core"; +// The generated artifact is also loaded directly by drizzle-kit, which resolves +// extensionless application TypeScript imports. Keep those imports portable for +// the consuming application's module resolver. +// @ts-ignore drizzle-kit resolves the extensionless TypeScript import. +import { user } from "./auth.generated"; +// @ts-ignore drizzle-kit resolves the extensionless TypeScript import. +import { payerUser } from "./billing-payer"; + +const timestamps = { + createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), + updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), +}; + +export const billingPriceEntries = pgTable( + "billing_price_entries", + { + id: uuid("id").primaryKey().defaultRandom(), + offerKey: text("offer_key").notNull(), + plan: text("plan").notNull(), + billingInterval: text("billing_interval").notNull(), + currency: text("currency").notNull(), + amountMinor: integer("amount_minor").notNull(), + providerTrialDays: integer("provider_trial_days").notNull().default(0), + provider: text("provider").notNull(), + providerProductId: text("provider_product_id").notNull(), + verifiedAt: timestamp("verified_at", { withTimezone: true }), + ...timestamps, + }, + (table) => ({ + providerProductUnique: uniqueIndex("billing_price_entries_provider_product_uidx").on( + table.provider, + table.providerProductId, + ), + offerKeyIdx: index("billing_price_entries_offer_key_idx").on(table.offerKey), + amountCheck: check("billing_price_entries_amount_check", sql`${table.amountMinor} > 0`), + trialDaysCheck: check("billing_price_entries_trial_days_check", sql`${table.providerTrialDays} >= 0`), + currencyCheck: check("billing_price_entries_currency_check", sql`${table.currency} ~ '^[A-Z]{3}$'`), + planCheck: check("billing_price_entries_plan_check", sql`${table.plan} IN ('pro')`), + intervalCheck: check( + "billing_price_entries_interval_check", + sql`${table.billingInterval} IN ('month', 'year')`, + ), + }), +); + +export const billingCatalogRevisions = pgTable( + "billing_catalog_revisions", + { + id: uuid("id").primaryKey().defaultRandom(), + revision: integer("revision").notNull().unique(), + checkoutProvider: text("checkout_provider").notNull(), + status: text("status").notNull().default("pending_verification"), + verifiedAt: timestamp("verified_at", { withTimezone: true }), + activatedAt: timestamp("activated_at", { withTimezone: true }), + retiredAt: timestamp("retired_at", { withTimezone: true }), + ...timestamps, + }, + (table) => ({ + statusCheck: check( + "billing_catalog_revisions_status_check", + sql`${table.status} IN ('pending_verification', 'active', 'retired', 'invalid', 'abandoned')`, + ), + revisionCheck: check("billing_catalog_revisions_revision_check", sql`${table.revision} > 0`), + activeProviderUnique: uniqueIndex("billing_catalog_revisions_active_provider_uidx") + .on(table.checkoutProvider) + .where(sql`${table.status} = 'active'`), + }), +); + +export const billingCatalogRevisionItems = pgTable( + "billing_catalog_revision_items", + { + id: uuid("id").primaryKey().defaultRandom(), + catalogRevisionId: uuid("catalog_revision_id") + .notNull() + .references(() => billingCatalogRevisions.id, { onDelete: "cascade" }), + offerKey: text("offer_key").notNull(), + billingPriceEntryId: uuid("billing_price_entry_id") + .notNull() + .references(() => billingPriceEntries.id, { onDelete: "restrict" }), + }, + (table) => ({ + revisionKeyUnique: uniqueIndex("billing_catalog_revision_items_revision_key_uidx").on( + table.catalogRevisionId, + table.offerKey, + ), + revisionPriceUnique: uniqueIndex("billing_catalog_revision_items_revision_price_uidx").on( + table.catalogRevisionId, + table.billingPriceEntryId, + ), + }), +); + +export const billingProviderCustomers = pgTable( + "billing_provider_customers", + { + id: uuid("id").primaryKey().defaultRandom(), + provider: text("provider").notNull(), + payerId: text("payer_id") + .notNull() + .references(() => payerUser.id, { onDelete: "restrict" }), + payerEmail: text("payer_email").notNull().default(""), + providerCustomerId: text("provider_customer_id"), + idempotencyKey: text("idempotency_key").notNull(), + status: text("status").notNull().default("creating"), + lastError: text("last_error"), + ...timestamps, + }, + (table) => ({ + providerPayerUnique: uniqueIndex("billing_provider_customers_provider_payer_uidx").on( + table.provider, + table.payerId, + ), + providerCustomerUnique: uniqueIndex("billing_provider_customers_provider_customer_uidx") + .on(table.provider, table.providerCustomerId) + .where(sql`${table.providerCustomerId} IS NOT NULL`), + idempotencyUnique: uniqueIndex("billing_provider_customers_idempotency_uidx").on( + table.idempotencyKey, + ), + statusCheck: check( + "billing_provider_customers_status_check", + sql`${table.status} IN ('creating', 'active', 'conflicted')`, + ), + }), +); + +export const billingCheckoutAttempts = pgTable( + "billing_checkout_attempts", + { + id: uuid("id").primaryKey().defaultRandom(), + attemptId: text("attempt_id").notNull().unique(), + billableEntityId: text("billable_entity_id") + .notNull() + .references(() => user.id, { onDelete: "restrict" }), + payerId: text("payer_id") + .notNull() + .references(() => payerUser.id, { onDelete: "restrict" }), + payerEmail: text("payer_email").notNull().default(""), + returnUrl: text("return_url").notNull().default(""), + provider: text("provider").notNull(), + catalogRevision: integer("catalog_revision").notNull(), + offerKey: text("offer_key").notNull(), + requestedPlan: text("requested_plan").notNull(), + requestedInterval: text("requested_interval").notNull(), + billingPriceEntryId: uuid("billing_price_entry_id") + .notNull() + .references(() => billingPriceEntries.id, { onDelete: "restrict" }), + quotedAmountMinor: integer("quoted_amount_minor").notNull(), + quotedCurrency: text("quoted_currency").notNull(), + billingCustomerId: uuid("billing_customer_id").references( + () => billingProviderCustomers.id, + { onDelete: "restrict" }, + ), + providerCheckoutSessionId: text("provider_checkout_session_id"), + checkoutUrlEncrypted: text("checkout_url_encrypted"), + idempotencyKey: text("idempotency_key").notNull(), + status: text("status").notNull().default("creating"), + expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(), + lastError: text("last_error"), + completedAt: timestamp("completed_at", { withTimezone: true }), + ...timestamps, + }, + (table) => ({ + providerSessionUnique: uniqueIndex("billing_checkout_attempts_provider_session_uidx") + .on(table.provider, table.providerCheckoutSessionId) + .where(sql`${table.providerCheckoutSessionId} IS NOT NULL`), + idempotencyUnique: uniqueIndex("billing_checkout_attempts_idempotency_uidx").on( + table.idempotencyKey, + ), + entityNonterminalUnique: uniqueIndex("billing_checkout_attempts_entity_nonterminal_uidx") + .on(table.billableEntityId) + .where(sql`${table.status} IN ('creating', 'open')`), + statusCheck: check( + "billing_checkout_attempts_status_check", + sql`${table.status} IN ('creating', 'open', 'completed', 'expired', 'abandoned', 'conflicted')`, + ), + amountCheck: check( + "billing_checkout_attempts_amount_check", + sql`${table.quotedAmountMinor} > 0`, + ), + planCheck: check( + "billing_checkout_attempts_plan_check", + sql`${table.requestedPlan} IN ('pro')`, + ), + intervalCheck: check( + "billing_checkout_attempts_interval_check", + sql`${table.requestedInterval} IN ('month', 'year')`, + ), + }), +); + +export const billingSubscriptions = pgTable( + "billing_subscriptions", + { + id: uuid("id").primaryKey().defaultRandom(), + billableEntityId: text("billable_entity_id") + .notNull() + .references(() => user.id, { onDelete: "restrict" }), + billingCustomerId: uuid("billing_customer_id") + .notNull() + .references(() => billingProviderCustomers.id, { onDelete: "restrict" }), + payerId: text("payer_id") + .notNull() + .references(() => payerUser.id, { onDelete: "restrict" }), + originCheckoutAttemptId: uuid("origin_checkout_attempt_id").references( + () => billingCheckoutAttempts.id, + { onDelete: "restrict" }, + ), + provider: text("provider").notNull(), + providerSubscriptionId: text("provider_subscription_id").notNull(), + providerProductId: text("provider_product_id").notNull(), + billingPriceEntryId: uuid("billing_price_entry_id") + .notNull() + .references(() => billingPriceEntries.id, { onDelete: "restrict" }), + catalogRevision: integer("catalog_revision").notNull(), + offerKey: text("offer_key").notNull(), + plan: text("plan").notNull(), + billingInterval: text("billing_interval").notNull(), + status: text("status").notNull().default("pending"), + currentPeriodStartsAt: timestamp("current_period_starts_at", { withTimezone: true }), + currentPeriodEndsAt: timestamp("current_period_ends_at", { withTimezone: true }), + paidThroughAt: timestamp("paid_through_at", { withTimezone: true }), + trialEndsAt: timestamp("trial_ends_at", { withTimezone: true }), + cancelAtPeriodEnd: boolean("cancel_at_period_end").notNull().default(false), + isEntitlementSource: boolean("is_entitlement_source").notNull().default(false), + providerOccurredAt: timestamp("provider_occurred_at", { withTimezone: true }), + providerVersion: text("provider_version"), + lastObservedAt: timestamp("last_observed_at", { withTimezone: true }), + lastReconciledAt: timestamp("last_reconciled_at", { withTimezone: true }), + ...timestamps, + }, + (table) => ({ + providerSubscriptionUnique: uniqueIndex("billing_subscriptions_provider_subscription_uidx").on( + table.provider, + table.providerSubscriptionId, + ), + entitySourceUnique: uniqueIndex("billing_subscriptions_entity_source_uidx") + .on(table.billableEntityId) + .where(sql`${table.isEntitlementSource} = true`), + statusCheck: check( + "billing_subscriptions_status_check", + sql`${table.status} IN ('pending', 'trialing', 'active', 'past_due', 'cancelled', 'expired')`, + ), + planCheck: check("billing_subscriptions_plan_check", sql`${table.plan} IN ('pro')`), + intervalCheck: check( + "billing_subscriptions_interval_check", + sql`${table.billingInterval} IN ('month', 'year')`, + ), + }), +); + +export const billingPlanChangeAttempts = pgTable( + "billing_plan_change_attempts", + { + id: uuid("id").primaryKey().defaultRandom(), + changeId: text("change_id").notNull().unique(), + billableEntityId: text("billable_entity_id") + .notNull() + .references(() => user.id, { onDelete: "restrict" }), + subscriptionId: uuid("subscription_id") + .notNull() + .references(() => billingSubscriptions.id, { onDelete: "restrict" }), + actorId: text("actor_id").notNull(), + payerId: text("payer_id") + .notNull() + .references(() => payerUser.id, { onDelete: "restrict" }), + provider: text("provider").notNull(), + idempotencyKey: text("idempotency_key").notNull(), + currentCatalogRevision: integer("current_catalog_revision").notNull(), + currentBillingPriceEntryId: uuid("current_billing_price_entry_id") + .notNull() + .references(() => billingPriceEntries.id, { onDelete: "restrict" }), + currentPlan: text("current_plan").notNull(), + currentInterval: text("current_interval").notNull(), + targetCatalogRevision: integer("target_catalog_revision").notNull(), + targetBillingPriceEntryId: uuid("target_billing_price_entry_id") + .notNull() + .references(() => billingPriceEntries.id, { onDelete: "restrict" }), + targetPlan: text("target_plan").notNull(), + targetInterval: text("target_interval").notNull(), + targetOfferKey: text("target_offer_key").notNull(), + effectiveAt: text("effective_at").notNull(), + prorationMode: text("proration_mode").notNull(), + providerPaymentId: text("provider_payment_id"), + paymentUrlEncrypted: text("payment_url_encrypted"), + status: text("status").notNull().default("creating"), + lastError: text("last_error"), + completedAt: timestamp("completed_at", { withTimezone: true }), + ...timestamps, + }, + (table) => ({ + idempotencyUnique: uniqueIndex("billing_plan_change_attempts_idempotency_uidx").on( + table.idempotencyKey, + ), + entityNonterminalUnique: uniqueIndex("billing_plan_change_attempts_entity_nonterminal_uidx") + .on(table.billableEntityId) + .where(sql`${table.status} IN ('creating', 'pending')`), + statusCheck: check( + "billing_plan_change_attempts_status_check", + sql`${table.status} IN ('creating', 'pending', 'succeeded', 'failed', 'conflicted')`, + ), + effectiveAtCheck: check( + "billing_plan_change_attempts_effective_at_check", + sql`${table.effectiveAt} IN ('immediately', 'next_billing_date')`, + ), + prorationModeCheck: check( + "billing_plan_change_attempts_proration_mode_check", + sql`${table.prorationMode} IN ('prorated_immediately', 'do_not_bill')`, + ), + currentPlanCheck: check( + "billing_plan_change_attempts_current_plan_check", + sql`${table.currentPlan} IN ('pro')`, + ), + targetPlanCheck: check( + "billing_plan_change_attempts_target_plan_check", + sql`${table.targetPlan} IN ('pro')`, + ), + }), +); + +export const billingPlanStates = pgTable( + "billing_plan_states", + { + id: uuid("id").primaryKey().defaultRandom(), + billableEntityId: text("billable_entity_id") + .notNull() + .unique() + .references(() => user.id, { onDelete: "restrict" }), + activeSubscriptionId: uuid("active_subscription_id").references( + () => billingSubscriptions.id, + { onDelete: "restrict" }, + ), + projectionVersion: integer("projection_version").notNull().default(0), + ...timestamps, + }, +); + +export const billingWebhookEvents = pgTable( + "billing_webhook_events", + { + id: uuid("id").primaryKey().defaultRandom(), + provider: text("provider").notNull(), + providerEventId: text("provider_event_id").notNull(), + eventType: text("event_type").notNull(), + occurredAt: timestamp("occurred_at", { withTimezone: true }).notNull(), + subscriptionId: text("subscription_id"), + checkoutAttemptId: text("checkout_attempt_id"), + payloadEncrypted: text("payload_encrypted"), + payloadKeyVersion: text("payload_key_version"), + verifiedKeyVersion: text("verified_key_version"), + status: text("status").notNull().default("pending"), + processingAttempts: integer("processing_attempts").notNull().default(0), + lastError: text("last_error"), + availableAt: timestamp("available_at", { withTimezone: true }).notNull().defaultNow(), + lockedAt: timestamp("locked_at", { withTimezone: true }), + leaseExpiresAt: timestamp("lease_expires_at", { withTimezone: true }), + workerId: text("worker_id"), + receivedAt: timestamp("received_at", { withTimezone: true }).notNull().defaultNow(), + processedAt: timestamp("processed_at", { withTimezone: true }), + }, + (table) => ({ + providerEventUnique: uniqueIndex("billing_webhook_events_provider_event_uidx").on( + table.provider, + table.providerEventId, + ), + queueIdx: index("billing_webhook_events_queue_idx").on(table.status, table.availableAt), + statusCheck: check( + "billing_webhook_events_status_check", + sql`${table.status} IN ('pending', 'processing', 'processed', 'ignored', 'quarantined', 'failed')`, + ), + }), +); + +export const billingReconciliationJobs = pgTable( + "billing_reconciliation_jobs", + { + id: uuid("id").primaryKey().defaultRandom(), + provider: text("provider").notNull(), + checkoutAttemptId: uuid("checkout_attempt_id").references( + () => billingCheckoutAttempts.id, + { onDelete: "restrict" }, + ), + planChangeAttemptId: uuid("plan_change_attempt_id").references( + () => billingPlanChangeAttempts.id, + { onDelete: "restrict" }, + ), + subscriptionId: uuid("subscription_id").references( + () => billingSubscriptions.id, + { onDelete: "restrict" }, + ), + providerCustomerId: uuid("provider_customer_id").references( + () => billingProviderCustomers.id, + { onDelete: "restrict" }, + ), + operation: text("operation").notNull().default("reconcile"), + status: text("status").notNull().default("pending"), + attemptCount: integer("attempt_count").notNull().default(0), + availableAt: timestamp("available_at", { withTimezone: true }).notNull().defaultNow(), + lockedAt: timestamp("locked_at", { withTimezone: true }), + leaseExpiresAt: timestamp("lease_expires_at", { withTimezone: true }), + workerId: text("worker_id"), + lastError: text("last_error"), + ...timestamps, + }, + (table) => ({ + exactlyOneSubject: check( + "billing_reconciliation_jobs_exactly_one_subject", + sql`((CASE WHEN ${table.checkoutAttemptId} IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN ${table.planChangeAttemptId} IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN ${table.subscriptionId} IS NOT NULL THEN 1 ELSE 0 END) + (CASE WHEN ${table.providerCustomerId} IS NOT NULL THEN 1 ELSE 0 END)) = 1`, + ), + liveCheckoutUnique: uniqueIndex("billing_reconciliation_jobs_live_checkout_uidx") + .on(table.checkoutAttemptId) + .where(sql`${table.checkoutAttemptId} IS NOT NULL AND ${table.status} IN ('pending', 'processing', 'failed')`), + livePlanChangeUnique: uniqueIndex("billing_reconciliation_jobs_live_plan_change_uidx") + .on(table.planChangeAttemptId) + .where(sql`${table.planChangeAttemptId} IS NOT NULL AND ${table.status} IN ('pending', 'processing', 'failed')`), + liveSubscriptionUnique: uniqueIndex("billing_reconciliation_jobs_live_subscription_uidx") + .on(table.subscriptionId) + .where(sql`${table.subscriptionId} IS NOT NULL AND ${table.status} IN ('pending', 'processing', 'failed')`), + liveCustomerUnique: uniqueIndex("billing_reconciliation_jobs_live_customer_uidx") + .on(table.providerCustomerId) + .where(sql`${table.providerCustomerId} IS NOT NULL AND ${table.status} IN ('pending', 'processing', 'failed')`), + statusCheck: check( + "billing_reconciliation_jobs_status_check", + sql`${table.status} IN ('pending', 'processing', 'failed', 'completed', 'quarantined')`, + ), + operationCheck: check( + "billing_reconciliation_jobs_operation_check", + sql`${table.operation} = 'reconcile' OR (${table.operation} = 'cancellation' AND ${table.subscriptionId} IS NOT NULL)`, + ), + }), +); diff --git a/apps/api/src/db/schema/domain.ts b/apps/api/src/db/schema/domain.ts new file mode 100644 index 00000000..605e3178 --- /dev/null +++ b/apps/api/src/db/schema/domain.ts @@ -0,0 +1,140 @@ +import { + bigint, + boolean, + jsonb, + pgTable, + text, + timestamp, + uniqueIndex, +} from "drizzle-orm/pg-core"; +import { sql } from "drizzle-orm"; +import { session, user } from "./auth.generated"; + +export const profiles = pgTable("profiles", { + userId: text("user_id") + .primaryKey() + .references(() => user.id, { onDelete: "cascade" }), + publicUserId: text("public_user_id").notNull().unique(), + active: boolean("active").notNull().default(true), + customerId: text("customer_id").unique(), + subscriptionId: text("subscription_id").unique(), + subscriptionEndsAfter: timestamp("subscription_ends_after", { + withTimezone: true, + }), + subscriptionMethod: text("subscription_method"), + subscriptionStatus: text("subscription_status") + .notNull() + .default("not-subscribed"), +}); + +export const apiKeys = pgTable( + "api_keys", + { + id: text("id").primaryKey(), + keyId: text("key_id").notNull().unique(), + name: text("name").notNull(), + key: text("key").notNull().unique(), + userId: text("user_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), + restriction: text("restriction"), + httpReferrers: text("http_referrers") + .array() + .notNull() + .default(sql`'{}'::text[]`), + ipAddresses: text("ip_addresses") + .array() + .notNull() + .default(sql`'{}'::text[]`), + deleted: boolean("deleted").notNull().default(false), + isDefault: boolean("is_default").notNull().default(false), + createdAt: timestamp("created_at", { withTimezone: true }).notNull(), + updatedAt: timestamp("updated_at", { withTimezone: true }).notNull(), + }, + (table) => ({ + userName: uniqueIndex("api_keys_user_name_uidx") + .on(table.userId, table.name) + .where(sql`${table.deleted} = false`), + oneDefault: uniqueIndex("api_keys_one_default_uidx") + .on(table.userId) + .where(sql`${table.isDefault} = true AND ${table.deleted} = false`), + }), +); + +export const media = pgTable("media", { + id: text("id").primaryKey(), + fileName: text("file_name").notNull(), + mediaId: text("media_id").notNull().unique(), + userId: text("user_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), + apikey: text("apikey").notNull(), + originalFileName: text("original_file_name").notNull(), + mimeType: text("mime_type").notNull(), + size: bigint("size", { mode: "number" }).notNull(), + thumbnailGenerated: boolean("thumbnail_generated").notNull().default(false), + accessControl: text("access_control").notNull().default("private"), + group: text("group_name"), + caption: text("caption"), + temp: boolean("temp").notNull().default(true), + createdAt: timestamp("created_at", { withTimezone: true }).notNull(), + updatedAt: timestamp("updated_at", { withTimezone: true }).notNull(), +}); + +export const mediaSettings = pgTable("media_settings", { + id: text("id").primaryKey(), + userId: text("user_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), + apikey: text("apikey").notNull().unique(), + useWebP: boolean("use_web_p"), + webpOutputQuality: bigint("webp_output_quality", { mode: "number" }), + thumbnailWidth: bigint("thumbnail_width", { mode: "number" }), + thumbnailHeight: bigint("thumbnail_height", { mode: "number" }), +}); + +export const signatures = pgTable("signatures", { + id: text("id").primaryKey(), + userId: text("user_id").notNull(), + apikey: text("apikey").notNull(), + signature: text("signature").notNull().unique(), + validTill: timestamp("valid_till", { withTimezone: true }).notNull(), + group: text("group_name"), + createdAt: timestamp("created_at", { withTimezone: true }).notNull(), +}); + +export const tusUploads = pgTable("tus_uploads", { + id: text("id").primaryKey(), + uploadId: text("upload_id").notNull().unique(), + userId: text("user_id").notNull(), + apikey: text("apikey").notNull(), + uploadLength: bigint("upload_length", { mode: "number" }).notNull(), + uploadOffset: bigint("upload_offset", { mode: "number" }) + .notNull() + .default(0), + metadata: jsonb("metadata").notNull(), + tempFilePath: text("temp_file_path"), + signature: text("signature"), + isComplete: boolean("is_complete").notNull().default(false), + expiresAt: timestamp("expires_at", { withTimezone: true }), + createdAt: timestamp("created_at", { withTimezone: true }).notNull(), + updatedAt: timestamp("updated_at", { withTimezone: true }).notNull(), +}); + +/** + * The app an OAuth end-user picked on `/oauth/select-app`, keyed by their + * Better Auth session. `oauthProvider`'s `postLogin.consentReferenceId` hook + * reads it back so the app ends up on the access token's `app_id` claim, which + * scopes MCP and REST requests made with that token to the app. + */ +export const oauthAppSelections = pgTable("oauth_app_selections", { + sessionId: text("session_id") + .primaryKey() + .references(() => session.id, { onDelete: "cascade" }), + apiKeyId: text("api_key_id") + .notNull() + .references(() => apiKeys.id, { onDelete: "cascade" }), + updatedAt: timestamp("updated_at", { withTimezone: true }) + .notNull() + .defaultNow(), +}); diff --git a/apps/api/src/db/schema/index.ts b/apps/api/src/db/schema/index.ts new file mode 100644 index 00000000..a38732fe --- /dev/null +++ b/apps/api/src/db/schema/index.ts @@ -0,0 +1,3 @@ +export * from "./auth.generated.js"; +export * from "./billing.generated.js"; +export * from "./domain.js"; diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index e53e4a32..254e1851 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -1,133 +1,70 @@ import { config as loadDotFile } from "dotenv"; loadDotFile(); -import express from "express"; -import connectToDatabase from "./config/db"; -import passport from "passport"; -import mediaRoutes from "./media/routes"; -import signatureRoutes from "./signature/routes"; -import mediaSettingsRoutes from "./media-settings/routes"; -import tusRoutes from "./tus/routes"; -import mcpRoutes from "./mcp/routes"; -import logger from "./services/log"; +import { checkDatabaseConnection } from "./config/db"; +import { closeMcpSessions } from "./mcp/routes"; +import logger, { shutdownObservability } from "./services/log"; import { createUser, findByEmail } from "./user/queries"; -import { Apikey, User } from "@medialit/models"; +import { User } from "@medialit/models"; import { getApiKeyByUserId } from "./apikey/queries"; -import swaggerUi from "swagger-ui-express"; -import swaggerOutput from "./swagger_output.json"; +import { closeDb, getDb, seedWebOAuthClient } from "@/db"; +import { sql } from "drizzle-orm"; +import type { GracefulShutdown } from "@codelitdev/platform"; +import { apiReadiness, createApiShutdown } from "./lifecycle"; import { spawn } from "child_process"; import { cleanupTUSUploads } from "./tus/cleanup"; import { cleanupExpiredTempUploads } from "./media/cleanup"; import { HOUR_IN_SECONDS } from "./config/constants"; +import { createMedialitAuth } from "./auth/better-auth"; +import { createApp } from "./app"; +import { startBillingMaintenance } from "./billing/maintenance"; -connectToDatabase(); -const app = express(); - -app.set("trust proxy", process.env.ENABLE_TRUST_PROXY === "true" ? 1 : false); - -app.use(express.json()); -app.use(express.urlencoded({ extended: false })); - -app.get( - "/health", - /* - #swagger.summary = 'Status of the server', - #swagger.description = 'Returns the status of the server and uptime' - #swagger.responses[200] = { - description: "OK", - content: { - "application/json": { - schema: { - type: "object", - properties: { - status: { - type: "string", - example: "ok", - }, - uptime: { - type: "number", - example: 12.345, - }, - }, - }, - }, - }, - } - */ - (req, res) => { - res.status(200).json({ - status: "ok", - uptime: process.uptime(), - }); - }, -); - -app.get( - "/openapi.json", - /* #swagger.ignore = true */ - (req, res) => { - res.json(swaggerOutput); - }, -); - -app.use( - "/docs", - swaggerUi.serve, - swaggerUi.setup(swaggerOutput, { - explorer: true, - swaggerOptions: { - persistAuthorization: true, - displayRequestDuration: true, - docExpansion: "none", - defaultModelsExpandDepth: -1, - validatorUrl: null, - }, - }), -); - -app.use("/settings/media", mediaSettingsRoutes(passport)); -app.use("/media/signature", signatureRoutes); -app.use("/media", tusRoutes); -app.use("/media", mediaRoutes); -app.use(mcpRoutes); - -app.get( - "/cleanup/temp", - /* #swagger.ignore = true */ - async (req, res) => { - await cleanupExpiredTempUploads(); - res.status(200).json({ - message: "Expired temp uploads cleaned up", - }); - }, -); -app.get( - "/cleanup/tus", - /* #swagger.ignore = true */ - async (req, res) => { - await cleanupTUSUploads(); - res.status(200).json({ - message: "Expired tus uploads cleaned up", - }); - }, -); +let started = false; +let shutdown: GracefulShutdown | undefined; const port = process.env.PORT || 80; -if (process.env.EMAIL) { - createAdminUser(); -} - checkConfig() + .then(() => checkDatabaseConnection()) .then(checkDependencies) - .then(() => { - app.listen(port, () => { + .then(async () => { + const publicApiUrl = ( + process.env.PUBLIC_API_URL || + process.env.API_SERVER || + `http://localhost:${port}` + ).replace(/\/$/, ""); + const webOrigin = ( + process.env.WEB_ORIGIN || + process.env.WEB_CLIENT || + "http://localhost:3000" + ).replace(/\/$/, ""); + const secret = process.env.BETTER_AUTH_SECRET!; + const auth = createMedialitAuth({ publicApiUrl, secret, webOrigin }); + const app = createApp({ + auth, + webOrigin, + readiness: () => + apiReadiness({ + started, + shuttingDown: shutdown?.shuttingDown() ?? false, + pingDatabase: () => getDb().execute(sql`select 1`), + }), + }); + await seedWebOAuthClient({ + redirectUris: [`${webOrigin}/api/auth/callback/medialit`], + }); + if (process.env.EMAIL) { + await createAdminUser(); + } + const stopBillingMaintenance = startBillingMaintenance(); + const server = app.listen(port, () => { + started = true; logger.info(`Medialit server running at ${port}`); }); // Setup background cleanup job for expired tus uploads - setInterval( + const tusCleanup = setInterval( async () => { await cleanupTUSUploads(); }, @@ -135,17 +72,49 @@ checkConfig() ); // Setup background cleanup job for expired temp uploads - setInterval( + const tempCleanup = setInterval( async () => { await cleanupExpiredTempUploads(); }, HOUR_IN_SECONDS, // 1 hour ); + + const apiShutdown = createApiShutdown({ + stopBackgroundJobs: () => { + clearInterval(tusCleanup); + clearInterval(tempCleanup); + stopBillingMaintenance(); + }, + closeMcpSessions, + closeServer: () => + new Promise((resolve, reject) => { + server.close((error) => + error ? reject(error) : resolve(), + ); + // Keep-alive sockets with no request would hold close open. + server.closeIdleConnections?.(); + }), + closeDatabase: closeDb, + flushObservability: () => shutdownObservability(1_000), + }); + shutdown = apiShutdown; + const handleSignal = (signal: NodeJS.Signals) => { + logger.info({ signal }, "Shutting down"); + apiShutdown + .shutdown() + .then(() => process.exit(0)) + .catch((error) => { + logger.error({ err: error }, "Graceful shutdown failed"); + process.exit(1); + }); + }; + process.on("SIGTERM", handleSignal); + process.on("SIGINT", handleSignal); }); async function checkConfig() { - if (!process.env.DB_CONNECTION_STRING) { - throw new Error("DB_CONNECTION_STRING is not set"); + if (!process.env.DATABASE_URL) { + throw new Error("DATABASE_URL is not set"); } if (!process.env.CLOUD_KEY || !process.env.CLOUD_SECRET) { throw new Error( @@ -169,11 +138,11 @@ async function checkConfig() { ); } if ( - !process.env.OAUTH_SIGNING_KEY || - Buffer.byteLength(process.env.OAUTH_SIGNING_KEY, "utf8") < 32 + !process.env.BETTER_AUTH_SECRET || + Buffer.byteLength(process.env.BETTER_AUTH_SECRET, "utf8") < 32 ) { throw new Error( - "OAUTH_SIGNING_KEY is required and must be at least 32 bytes (256 bits). " + + "BETTER_AUTH_SECRET is required and must be at least 32 bytes (256 bits). " + "Generate one with: openssl rand -base64 48", ); } diff --git a/apps/api/src/lifecycle.ts b/apps/api/src/lifecycle.ts new file mode 100644 index 00000000..16d187c7 --- /dev/null +++ b/apps/api/src/lifecycle.ts @@ -0,0 +1,68 @@ +import { + createGracefulShutdown, + type GracefulShutdown, + type ReadinessReport, + readinessReport, +} from "@codelitdev/platform"; + +/** Stays under Docker's default 10s stop grace period. */ +export const SHUTDOWN_TIMEOUT_MS = 8_000; + +export type ShutdownSteps = { + /** Stops timers that start new work (cleanup jobs, billing batches). */ + stopBackgroundJobs: () => void; + /** Ends open MCP streams, which would otherwise keep the server open. */ + closeMcpSessions: () => Promise; + /** Stops accepting connections and waits for in-flight requests. */ + closeServer: () => Promise; + closeDatabase: () => Promise; + flushObservability: () => Promise; +}; + +/** + * The kernel runs hooks concurrently, so the ordered steps share one hook: + * the database must outlive in-flight requests, and logs flush last. + */ +export function createApiShutdown( + steps: ShutdownSteps, + timeoutMs = SHUTDOWN_TIMEOUT_MS, +): GracefulShutdown { + return createGracefulShutdown({ + timeoutMs, + hooks: [ + async () => { + steps.stopBackgroundJobs(); + await steps.closeMcpSessions(); + await steps.closeServer(); + await steps.closeDatabase(); + await steps.flushObservability(); + }, + ], + }); +} + +/** + * Not ready until startup completes, and again once shutdown starts, so a + * load balancer stops routing before connections close. + */ +export async function apiReadiness(input: { + started: boolean; + shuttingDown: boolean; + pingDatabase: () => Promise; +}): Promise { + const accepting = input.started && !input.shuttingDown; + let database = false; + if (accepting) { + try { + await input.pingDatabase(); + database = true; + } catch { + database = false; + } + } + return readinessReport([ + { name: "started", ready: input.started }, + { name: "accepting_requests", ready: !input.shuttingDown }, + { name: "database", ready: database }, + ]); +} diff --git a/apps/api/src/mcp/auth-context.ts b/apps/api/src/mcp/auth-context.ts new file mode 100644 index 00000000..0fa04840 --- /dev/null +++ b/apps/api/src/mcp/auth-context.ts @@ -0,0 +1,77 @@ +import type { AuthInfo } from "@modelcontextprotocol/sdk/server/auth/types.js"; + +export type MedialitMcpAuthExtra = { + authKind: "oauth" | "apikey"; + /** Internal id of the authenticated account. */ + userId: string; + user: any; + /** The app's API key. OAuth requests act on the app picked at + * authorization; API-key requests act on the key's own app. */ + apikey: string; +}; + +export type MedialitMcpAuthInfo = AuthInfo & { + extra: MedialitMcpAuthExtra; +}; + +type McpAuthRequest = { + authKind?: unknown; + userId?: unknown; + clientId?: unknown; + apikey?: unknown; + scopes?: unknown; + user?: any; +}; + +/** Builds the auth info the MCP transport hands to tool handlers from the + * fields `mcpAuth` sets on the request. */ +export function createMcpAuthInfo(input: unknown): MedialitMcpAuthInfo | null { + if (!input || typeof input !== "object") return null; + const req = input as McpAuthRequest; + if ( + (req.authKind !== "oauth" && req.authKind !== "apikey") || + typeof req.userId !== "string" || + !req.userId || + typeof req.apikey !== "string" || + !req.apikey || + !req.user + ) { + return null; + } + const scopes = Array.isArray(req.scopes) + ? req.scopes.filter( + (scope): scope is string => typeof scope === "string", + ) + : []; + const clientId = + req.authKind === "oauth" && typeof req.clientId === "string" + ? req.clientId + : "apikey"; + return { + // The SDK requires a token. Never put a credential secret here. + token: `${req.authKind}:${clientId}`, + clientId, + scopes, + extra: { + authKind: req.authKind, + userId: req.userId, + user: req.user, + apikey: req.apikey, + }, + }; +} + +/** The authenticated account and app for a tool call, or `null`. */ +export function getMcpAuth(extra: any): MedialitMcpAuthExtra | null { + const auth = extra?.authInfo?.extra as MedialitMcpAuthExtra | undefined; + if ( + !auth || + (auth.authKind !== "oauth" && auth.authKind !== "apikey") || + !auth.userId || + !auth.apikey || + !auth.user + ) { + return null; + } + return auth; +} diff --git a/apps/api/src/mcp/routes.ts b/apps/api/src/mcp/routes.ts index 827ad494..e14b1964 100644 --- a/apps/api/src/mcp/routes.ts +++ b/apps/api/src/mcp/routes.ts @@ -1,8 +1,9 @@ import { Router } from "express"; import rateLimit from "express-rate-limit"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { mcpCorsHeaders, patchMcpAccept } from "@codelitdev/mcp-server-kit"; import { mcpAuth } from "../auth/middleware"; -import { oauthRouter } from "../oauth/routes"; +import { createMcpAuthInfo } from "./auth-context"; import { createMCPSession } from "./server"; const router = Router(); @@ -21,14 +22,12 @@ const mcpLimiter = rateLimit({ const mcpSessions = new Map(); const mcpCors = (req: any, res: any, next: any) => { - const origin = req.headers.origin || "*"; - res.header("Access-Control-Allow-Origin", origin); - res.header("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS"); - res.header( - "Access-Control-Allow-Headers", - "Content-Type, Accept, Mcp-Session-Id, Mcp-Protocol-Version, x-medialit-apikey, Authorization", - ); - res.header("Access-Control-Expose-Headers", "Mcp-Session-Id"); + const headers = mcpCorsHeaders(req.headers); + headers["Access-Control-Allow-Headers"] = + `${headers["Access-Control-Allow-Headers"]}, x-medialit-apikey`; + for (const [name, value] of Object.entries(headers)) { + res.header(name, value); + } if (req.method === "OPTIONS") { return res.status(204).end(); } @@ -36,17 +35,11 @@ const mcpCors = (req: any, res: any, next: any) => { }; function patchMcpAcceptHeaders(req: any) { - const accept = req.headers.accept || ""; - const needsJson = !accept.includes("application/json"); - const needsSSE = !accept.includes("text/event-stream"); - if (!needsJson && !needsSSE) return; - - const additions: string[] = []; - if (needsJson) additions.push("application/json"); - if (needsSSE) additions.push("text/event-stream"); - const newAccept = accept - ? `${accept}, ${additions.join(", ")}` - : additions.join(", "); + const patched = patchMcpAccept(req.headers); + const newAccept = Array.isArray(patched.accept) + ? patched.accept.join(", ") + : patched.accept; + if (!newAccept || newAccept === req.headers.accept) return; req.headers.accept = newAccept; const rawHeaders: string[] = req.rawHeaders; @@ -61,27 +54,31 @@ function patchMcpAcceptHeaders(req: any) { if (!found) rawHeaders.push("Accept", newAccept); } -function getMcpAuth(req: any) { - return { - token: req.apikey || "", - clientId: String(req.userId || req.user?._id || req.user?.id || ""), - user: req.user, - scopes: [] as string[], - }; +/** Runs after `mcpAuth`, which accepts exactly one of an OAuth bearer token + * or an `x-medialit-apikey` header. */ +function attachMcpAuthInfo(req: any, res: any, next: any) { + const authInfo = createMcpAuthInfo(req); + if (!authInfo) { + return res.status(401).json({ + error: "unauthorized", + error_description: "MCP authentication context is incomplete.", + }); + } + req.auth = authInfo; + next(); } router.use(["/.well-known", "/oauth"], mcpCors); -router.use(oauthRouter); router.post( "/mcp", mcpCors, mcpLimiter, mcpAuth, + attachMcpAuthInfo, async (req: any, res: any) => { patchMcpAcceptHeaders(req); - const auth = getMcpAuth(req); const sessionId = req.headers["mcp-session-id"] as string | undefined; if (sessionId) { @@ -93,25 +90,53 @@ router.post( id: null, }); } - await transport.handleRequest( - Object.assign(req, { auth }), - res, - req.body, - ); + await transport.handleRequest(req, res, req.body); } else { const transport = createMCPSession( (id) => mcpSessions.set(id, transport), (id) => mcpSessions.delete(id), ); - await transport.handleRequest( - Object.assign(req, { auth }), - res, - req.body, - ); + await transport.handleRequest(req, res, req.body); + } + }, +); + +router.delete( + "/mcp", + mcpCors, + mcpLimiter, + mcpAuth, + attachMcpAuthInfo, + async (req: any, res: any) => { + const sessionId = req.headers["mcp-session-id"] as string | undefined; + if (!sessionId) { + return res.status(400).json({ + jsonrpc: "2.0", + error: { code: -32600, message: "Session ID required" }, + id: null, + }); } + + const transport = mcpSessions.get(sessionId); + if (!transport) { + return res.status(404).json({ + jsonrpc: "2.0", + error: { code: -32001, message: "Session not found" }, + id: null, + }); + } + + await transport.handleRequest(req, res); }, ); router.options("/mcp", mcpCors); +/** Closes open MCP sessions so their streams do not hold the server open. */ +export async function closeMcpSessions(): Promise { + const transports = [...mcpSessions.values()]; + mcpSessions.clear(); + await Promise.allSettled(transports.map((transport) => transport.close())); +} + export default router; diff --git a/apps/api/src/mcp/server.ts b/apps/api/src/mcp/server.ts index bf40f85d..88b4c3ec 100644 --- a/apps/api/src/mcp/server.ts +++ b/apps/api/src/mcp/server.ts @@ -1,16 +1,74 @@ import crypto from "crypto"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ToolAnnotations } from "@modelcontextprotocol/sdk/types.js"; +import type { AnyZodObject, ZodRawShape } from "zod"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import { registerMediaTools } from "./tools/media"; import { registerSignatureTool } from "./tools/signature"; import { registerSettingsTools } from "./tools/settings"; import { registerUploadTool } from "./tools/upload"; +import { registerWhoamiTool } from "./tools/whoami"; +import { hasScope, insufficientScopeMessage, toolScope } from "../auth/scopes"; -function registerAllTools(server: McpServer): void { - registerMediaTools(server); - registerSignatureTool(server); - registerSettingsTools(server); - registerUploadTool(server); +export type McpToolConfig = { + description?: string; + inputSchema?: ZodRawShape; + outputSchema?: ZodRawShape | AnyZodObject; + annotations?: ToolAnnotations; +}; + +export type McpToolHandler = (args: any, extra: any) => unknown; + +export interface McpToolRegistrar { + registerTool( + name: string, + config: McpToolConfig, + handler: McpToolHandler, + ): void; +} + +/** + * Registers tools on `server` so every tool checks the caller's OAuth scopes: + * tools annotated `readOnlyHint: true` need data:read, all others need + * data:write. API keys carry both scopes. + * + * The SDK calls handlers of tools without an input schema with only the + * request extra. Registered handlers are always called as `(args, extra)`. + */ +export function createToolRegistrar(server: McpServer): McpToolRegistrar { + return { + registerTool(name, config, handler) { + const scope = toolScope(config.annotations?.readOnlyHint); + const hasInput = config.inputSchema !== undefined; + const register = server.registerTool.bind(server) as ( + ...args: any[] + ) => unknown; + register(name, config, (...callArgs: any[]) => { + const [args, extra] = hasInput ? callArgs : [{}, callArgs[0]]; + if (!hasScope(extra?.authInfo?.scopes, scope)) { + return { + content: [ + { + type: "text" as const, + text: insufficientScopeMessage(scope), + }, + ], + isError: true, + }; + } + return handler(args, extra); + }); + }, + }; +} + +export function registerAllTools(server: McpServer): void { + const tools = createToolRegistrar(server); + registerWhoamiTool(tools); + registerMediaTools(tools); + registerSignatureTool(tools); + registerSettingsTools(tools); + registerUploadTool(tools); } /** @@ -32,7 +90,7 @@ export function createMCPSession( name: "MediaLit", version: "1.0.0", description: - "MediaLit MCP server — manage media files, storage, and upload settings for a MediaLit account. Supports listing, inspecting, deleting, and sealing media items, querying storage usage, generating upload signatures, and configuring media processing settings.", + "MediaLit MCP server — manage media files, storage, and upload settings for one MediaLit app: the app picked when the client was authorized, or the API key's app. Supports listing, inspecting, deleting, and sealing media items, checking the connected account and app (whoami), querying storage usage, generating upload signatures, and configuring media processing settings.", }); registerAllTools(server); server.connect(transport); diff --git a/apps/api/src/mcp/tools/media.ts b/apps/api/src/mcp/tools/media.ts index 2f51af0f..3eac83e8 100644 --- a/apps/api/src/mcp/tools/media.ts +++ b/apps/api/src/mcp/tools/media.ts @@ -1,13 +1,10 @@ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { McpToolRegistrar } from "../server"; import { z } from "zod"; -import { - maxStorageAllowedNotSubscribed, - maxStorageAllowedSubscribed, -} from "../../config/constants"; import mediaService from "../../media/service"; import * as mediaQueries from "../../media/queries"; -import { getSubscriptionStatus } from "@medialit/models"; +import { maxStorageFor } from "../../billing/entitlements"; import { NOT_FOUND, SUCCESS } from "../../config/strings"; +import { getMcpAuth } from "../auth-context"; import { AUTH_ERROR, INTERNAL_ERROR } from "./responses"; import { mediaListSchema, @@ -16,7 +13,7 @@ import { successMessageSchema, } from "./schemas"; -export function registerMediaTools(server: McpServer): void { +export function registerMediaTools(server: McpToolRegistrar): void { server.registerTool( "list_media", { @@ -50,11 +47,9 @@ export function registerMediaTools(server: McpServer): void { }, }, async (args: any, extra: any) => { - const userId = extra.authInfo?.user?._id; - const apikey = extra.authInfo?.token; - if (!userId || !apikey) { - return AUTH_ERROR; - } + const auth = getMcpAuth(extra); + if (!auth) return AUTH_ERROR; + const { userId, apikey } = auth; try { const currentPage = args.page || 1; const recordsPerPage = args.limit || 10; @@ -95,11 +90,9 @@ export function registerMediaTools(server: McpServer): void { }, }, async (args: any, extra: any) => { - const userId = extra.authInfo?.clientId; - const apikey = extra.authInfo?.token; - if (!userId || !apikey) { - return AUTH_ERROR; - } + const auth = getMcpAuth(extra); + if (!auth) return AUTH_ERROR; + const { userId, apikey } = auth; try { const media = await mediaService.getMediaDetails({ userId, @@ -133,7 +126,7 @@ export function registerMediaTools(server: McpServer): void { "get_media_count", { description: - "Returns the total number of media items in the default app.", + "Returns the total number of media items in the connected app.", outputSchema: z.object({ count: z.number() }), annotations: { readOnlyHint: true, @@ -142,12 +135,10 @@ export function registerMediaTools(server: McpServer): void { destructiveHint: false, }, }, - async (extra: any) => { - const userId = extra.authInfo?.user?._id; - const apikey = extra.authInfo?.token; - if (!userId || !apikey) { - return AUTH_ERROR; - } + async (_args: any, extra: any) => { + const auth = getMcpAuth(extra); + if (!auth) return AUTH_ERROR; + const { userId, apikey } = auth; try { const count = await mediaQueries.getMediaCount({ userId, @@ -172,7 +163,7 @@ export function registerMediaTools(server: McpServer): void { "get_total_storage", { description: - "Returns the total storage used by the default app and the account storage limit, both in bytes.", + "Returns the total storage used by the connected app and the account storage limit, both in bytes.", outputSchema: storageSchema, annotations: { readOnlyHint: true, @@ -200,11 +191,9 @@ export function registerMediaTools(server: McpServer): void { }, }, async (args: any, extra: any) => { - const userId = extra.authInfo?.clientId; - const apikey = extra.authInfo?.token; - if (!userId || !apikey) { - return AUTH_ERROR; - } + const auth = getMcpAuth(extra); + if (!auth) return AUTH_ERROR; + const { userId, apikey } = auth; try { await mediaService.deleteMedia({ userId, @@ -243,11 +232,9 @@ export function registerMediaTools(server: McpServer): void { }, }, async (args: any, extra: any) => { - const userId = extra.authInfo?.clientId; - const apikey = extra.authInfo?.token; - if (!userId || !apikey) { - return AUTH_ERROR; - } + const auth = getMcpAuth(extra); + if (!auth) return AUTH_ERROR; + const { userId, apikey } = auth; try { const media = await mediaService.sealMedia({ userId, @@ -268,22 +255,18 @@ export function registerMediaTools(server: McpServer): void { } export async function handleGetTotalStorageTool( + _args: unknown, extra: any, dependencies = { getTotalSpace: mediaQueries.getTotalSpace }, ) { - const user = extra.authInfo?.user; - const userId = user?._id; - const apikey = extra.authInfo?.token; - if (!userId || !apikey) { - return AUTH_ERROR; - } + const auth = getMcpAuth(extra); + if (!auth) return AUTH_ERROR; + const { user, userId, apikey } = auth; try { const storage = await dependencies.getTotalSpace({ userId, apikey }); const response = { storage, - maxStorage: getSubscriptionStatus(user) - ? maxStorageAllowedSubscribed - : maxStorageAllowedNotSubscribed, + maxStorage: maxStorageFor(user), }; return { content: [ diff --git a/apps/api/src/mcp/tools/schemas.ts b/apps/api/src/mcp/tools/schemas.ts index 560666ca..35fbca01 100644 --- a/apps/api/src/mcp/tools/schemas.ts +++ b/apps/api/src/mcp/tools/schemas.ts @@ -39,3 +39,16 @@ export const successMessageSchema = z.object({ export const signatureSchema = z.object({ signature: z.string(), }); + +export const whoamiSchema = z.object({ + auth: z.enum(["oauth", "apikey"]), + email: z.string(), + app: z.object({ + id: z.string(), + name: z.string(), + default: z.boolean(), + }), + files: z.number(), + storage: z.number(), + maxStorage: z.number(), +}); diff --git a/apps/api/src/mcp/tools/settings.ts b/apps/api/src/mcp/tools/settings.ts index 9a6a9cb3..f92183fe 100644 --- a/apps/api/src/mcp/tools/settings.ts +++ b/apps/api/src/mcp/tools/settings.ts @@ -1,18 +1,19 @@ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { McpToolRegistrar } from "../server"; import { z } from "zod"; import { getMediaSettings } from "../../media-settings/service"; import { updateMediaSettings } from "../../media-settings/queries"; import { SUCCESS } from "../../config/strings"; +import { getMcpAuth } from "../auth-context"; import { AUTH_ERROR, INTERNAL_ERROR } from "./responses"; import { mediaSettingsSchema, successMessageSchema } from "./schemas"; -export function registerSettingsTools(server: McpServer): void { +export function registerSettingsTools(server: McpToolRegistrar): void { // get_media_settings server.registerTool( "get_media_settings", { description: - "Returns the current image processing configuration for the account, including WebP conversion settings and thumbnail dimensions.", + "Returns the current image processing configuration for the connected app, including WebP conversion settings and thumbnail dimensions.", outputSchema: mediaSettingsSchema, annotations: { readOnlyHint: true, @@ -21,12 +22,10 @@ export function registerSettingsTools(server: McpServer): void { destructiveHint: false, }, }, - async (extra: any) => { - const userId = extra.authInfo?.clientId; - const apikey = extra.authInfo?.token; - if (!userId || !apikey) { - return AUTH_ERROR; - } + async (_args: any, extra: any) => { + const auth = getMcpAuth(extra); + if (!auth) return AUTH_ERROR; + const { userId, apikey } = auth; try { const settings = await getMediaSettings(userId, apikey); return { @@ -49,7 +48,7 @@ export function registerSettingsTools(server: McpServer): void { "update_media_settings", { description: - "Overwrites image processing settings for the account. Supply only the fields you want to change; omitted fields retain their current values.", + "Overwrites image processing settings for the connected app. Supply only the fields you want to change; omitted fields retain their current values.", inputSchema: { useWebP: z .boolean() @@ -83,11 +82,9 @@ export function registerSettingsTools(server: McpServer): void { }, }, async (args: any, extra: any) => { - const userId = extra.authInfo?.clientId; - const apikey = extra.authInfo?.token; - if (!userId || !apikey) { - return AUTH_ERROR; - } + const auth = getMcpAuth(extra); + if (!auth) return AUTH_ERROR; + const { userId, apikey } = auth; try { await updateMediaSettings({ userId, diff --git a/apps/api/src/mcp/tools/signature.ts b/apps/api/src/mcp/tools/signature.ts index 0a1418a7..acc4dd64 100644 --- a/apps/api/src/mcp/tools/signature.ts +++ b/apps/api/src/mcp/tools/signature.ts @@ -1,10 +1,11 @@ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { McpToolRegistrar } from "../server"; import { z } from "zod"; import { generateSignature } from "../../signature/service"; +import { getMcpAuth } from "../auth-context"; import { AUTH_ERROR, INTERNAL_ERROR } from "./responses"; import { signatureSchema } from "./schemas"; -export function registerSignatureTool(server: McpServer): void { +export function registerSignatureTool(server: McpToolRegistrar): void { server.registerTool( "create_upload_signature", { @@ -27,11 +28,9 @@ export function registerSignatureTool(server: McpServer): void { }, }, async (args: any, extra: any) => { - const userId = extra.authInfo?.clientId; - const apikey = extra.authInfo?.token; - if (!userId || !apikey) { - return AUTH_ERROR; - } + const auth = getMcpAuth(extra); + if (!auth) return AUTH_ERROR; + const { userId, apikey } = auth; try { const signature = await generateSignature({ userId, diff --git a/apps/api/src/mcp/tools/upload.ts b/apps/api/src/mcp/tools/upload.ts index 182367a0..d8ad4105 100644 --- a/apps/api/src/mcp/tools/upload.ts +++ b/apps/api/src/mcp/tools/upload.ts @@ -1,10 +1,11 @@ import os from "os"; import path from "path"; import { writeFile, mkdir, copyFile, mkdtemp, rm } from "fs/promises"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { McpToolRegistrar } from "../server"; import { z } from "zod"; import mediaService from "../../media/service"; import { validateUploadConstraints } from "../../media/storage-middleware"; +import { getMcpAuth } from "../auth-context"; import { AUTH_ERROR } from "./responses"; import { mediaSchema } from "./schemas"; @@ -47,7 +48,7 @@ function isValidBase64(str: string): boolean { return /^[A-Za-z0-9+/]*={0,2}$/.test(str); } -export function registerUploadTool(server: McpServer): void { +export function registerUploadTool(server: McpToolRegistrar): void { server.registerTool( "upload_media", { @@ -91,12 +92,9 @@ export function registerUploadTool(server: McpServer): void { } export async function handleUploadMediaTool(args: any, extra: any) { - const userId = extra?.authInfo?.clientId; - const apikey = extra?.authInfo?.token; - const user = extra?.authInfo?.user; - if (!userId || !apikey || !user) { - return AUTH_ERROR; - } + const auth = getMcpAuth(extra); + if (!auth) return AUTH_ERROR; + const { user, userId, apikey } = auth; // Validate base64 if (!isValidBase64(args.fileBase64)) { diff --git a/apps/api/src/mcp/tools/whoami.ts b/apps/api/src/mcp/tools/whoami.ts new file mode 100644 index 00000000..e26044f4 --- /dev/null +++ b/apps/api/src/mcp/tools/whoami.ts @@ -0,0 +1,68 @@ +import { getApiKeyUsingKeyId } from "../../apikey/queries"; +import { maxStorageFor } from "../../billing/entitlements"; +import * as mediaQueries from "../../media/queries"; +import { getMcpAuth } from "../auth-context"; +import type { McpToolRegistrar } from "../server"; +import { AUTH_ERROR, INTERNAL_ERROR } from "./responses"; +import { whoamiSchema } from "./schemas"; + +export function registerWhoamiTool(server: McpToolRegistrar): void { + server.registerTool( + "whoami", + { + description: + "Returns the signed-in account, the app this connection manages files in, and that app's file count and storage use against the account limit (bytes).", + outputSchema: whoamiSchema, + annotations: { + readOnlyHint: true, + idempotentHint: true, + openWorldHint: false, + destructiveHint: false, + }, + }, + handleWhoamiTool, + ); +} + +export async function handleWhoamiTool( + _args: unknown, + extra: any, + dependencies = { + getApiKey: getApiKeyUsingKeyId, + getMediaCount: mediaQueries.getMediaCount, + getTotalSpace: mediaQueries.getTotalSpace, + }, +) { + const auth = getMcpAuth(extra); + if (!auth) return AUTH_ERROR; + const { authKind, user, userId, apikey } = auth; + try { + const [app, files, storage] = await Promise.all([ + dependencies.getApiKey(apikey), + dependencies.getMediaCount({ userId, apikey }), + dependencies.getTotalSpace({ userId, apikey }), + ]); + if (!app) return AUTH_ERROR; + // Identify the app by its public id; never return the key itself. + const response = { + auth: authKind, + email: user.email, + app: { + id: app.keyId, + name: app.name, + default: Boolean(app.default), + }, + files, + storage, + maxStorage: maxStorageFor(user), + }; + return { + content: [ + { type: "text" as const, text: JSON.stringify(response) }, + ], + structuredContent: response, + }; + } catch { + return INTERNAL_ERROR; + } +} diff --git a/apps/api/src/media-settings/model.ts b/apps/api/src/media-settings/model.ts index 4954038d..5cd1285b 100644 --- a/apps/api/src/media-settings/model.ts +++ b/apps/api/src/media-settings/model.ts @@ -1,22 +1,8 @@ -import mongoose from "mongoose"; - export interface MediaSettings { - userId: mongoose.Types.ObjectId | undefined; + userId: string | undefined; apikey: string; useWebP?: boolean; webpOutputQuality?: number; thumbnailWidth?: number; thumbnailHeight?: number; } - -const MediaSettingsSchema = new mongoose.Schema({ - userId: { type: mongoose.Schema.Types.ObjectId, required: true }, - apikey: { type: String, required: true, unique: true }, - useWebP: Boolean, - webpOutputQuality: Number, - thumbnailWidth: Number, - thumbnailHeight: Number, -}); - -export default mongoose.models.MediaSettings || - mongoose.model("MediaSettings", MediaSettingsSchema); diff --git a/apps/api/src/media-settings/queries.ts b/apps/api/src/media-settings/queries.ts index b5a3a71c..e9b60104 100644 --- a/apps/api/src/media-settings/queries.ts +++ b/apps/api/src/media-settings/queries.ts @@ -1,11 +1,12 @@ -import MediaSettingsModel, { MediaSettings } from "./model"; +import { MediaSettings } from "./model"; import { UpdateMediaSettingsProps } from "./service"; +import { getMediaSettingsRecord, upsertMediaSettings } from "@/db"; export async function getMediaSettings( userId: string, apikey: string, ): Promise { - return await MediaSettingsModel.findOne({ userId, apikey }); + return getMediaSettingsRecord(userId, apikey); } export async function updateMediaSettings({ @@ -16,16 +17,12 @@ export async function updateMediaSettings({ thumbnailWidth, thumbnailHeight, }: UpdateMediaSettingsProps): Promise { - await MediaSettingsModel.findOneAndUpdate( - { userId, apikey }, - { - $set: { - useWebP, - webpOutputQuality, - thumbnailWidth, - thumbnailHeight, - }, - }, - { upsert: true }, - ); + await upsertMediaSettings({ + userId, + apikey, + useWebP, + webpOutputQuality, + thumbnailWidth, + thumbnailHeight, + }); } diff --git a/apps/api/src/media-settings/routes.ts b/apps/api/src/media-settings/routes.ts index 89f1cb97..af2f49fa 100644 --- a/apps/api/src/media-settings/routes.ts +++ b/apps/api/src/media-settings/routes.ts @@ -5,6 +5,8 @@ import { } from "./handlers"; import apikey from "../apikey/middleware"; import { authenticatedApiLimiter } from "../auth/limiters"; +import { DATA_READ_SCOPE, DATA_WRITE_SCOPE } from "../auth/options"; +import { requireScope } from "../auth/scopes"; export default (passport: any) => { const router = express.Router(); @@ -43,6 +45,7 @@ export default (passport: any) => { */ authenticatedApiLimiter, apikey, + requireScope(DATA_WRITE_SCOPE), updateMediaSettingsHandler, ); @@ -66,6 +69,7 @@ export default (passport: any) => { */ authenticatedApiLimiter, apikey, + requireScope(DATA_READ_SCOPE), getMediaSettingsHandler, ); diff --git a/apps/api/src/media/GetPageProps.ts b/apps/api/src/media/GetPageProps.ts index 9bdfb50b..2e7c5dd3 100644 --- a/apps/api/src/media/GetPageProps.ts +++ b/apps/api/src/media/GetPageProps.ts @@ -1,11 +1,10 @@ import { AccessControl } from "@medialit/models"; -import mongoose from "mongoose"; export default interface GetPageProps { - userId: mongoose.Types.ObjectId; + userId: string; apikey: string; - access: AccessControl; - page: number; - recordsPerPage: number; + page?: number; + access?: AccessControl; group?: string; + recordsPerPage?: number; } diff --git a/apps/api/src/media/cleanup.ts b/apps/api/src/media/cleanup.ts index 3a2223ad..41089d75 100644 --- a/apps/api/src/media/cleanup.ts +++ b/apps/api/src/media/cleanup.ts @@ -1,5 +1,5 @@ import logger from "../services/log"; -import MediaModel from "./model"; +import { deleteMediaById, listExpiredTempMedia } from "@/db"; import { deleteFolder } from "../services/s3"; import { PATH_PREFIX, @@ -14,10 +14,7 @@ export async function cleanupExpiredTempUploads(): Promise { ); try { - const expired = await MediaModel.find({ - temp: true, - createdAt: { $lt: cutoff }, - }).lean(); + const expired = await listExpiredTempMedia(cutoff); if (expired.length === 0) { logger.info("No expired temp uploads found to cleanup"); @@ -37,7 +34,7 @@ export async function cleanupExpiredTempUploads(): Promise { await deleteFolder(tmpPrefix, cloudBucket); // Delete media record - await MediaModel.deleteOne({ _id: media._id }); + await deleteMediaById(media.id); count++; } catch (err: any) { logger.error( diff --git a/apps/api/src/media/handlers.ts b/apps/api/src/media/handlers.ts index dd9848e9..37a50cca 100644 --- a/apps/api/src/media/handlers.ts +++ b/apps/api/src/media/handlers.ts @@ -1,14 +1,10 @@ import Joi from "joi"; -import { - maxStorageAllowedNotSubscribed, - maxStorageAllowedSubscribed, -} from "../config/constants"; import { FILE_IS_REQUIRED, NOT_FOUND, SUCCESS } from "../config/strings"; import logger from "../services/log"; import { Request } from "express"; import mediaService from "./service"; import { getMediaCount as getCount, getTotalSpace } from "./queries"; -import { getSubscriptionStatus } from "@medialit/models"; +import { maxStorageFor } from "../billing/entitlements"; import { getSignatureFromReq } from "../signature/utils"; import { getMediaSchema, uploadMediaSchema } from "./schemas"; @@ -113,9 +109,7 @@ export async function getTotalSpaceOccupied(req: any, res: any) { const totalSpaceOccupied = await getTotalSpace({ userId, apikey }); return res.status(200).json({ storage: totalSpaceOccupied, - maxStorage: getSubscriptionStatus(req.user) - ? maxStorageAllowedSubscribed - : maxStorageAllowedNotSubscribed, + maxStorage: maxStorageFor(req.user), }); } catch (err: any) { return res.status(500).json({ error: err.message }); diff --git a/apps/api/src/media/model.ts b/apps/api/src/media/model.ts deleted file mode 100644 index 68ba5d60..00000000 --- a/apps/api/src/media/model.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { MediaSchema } from "@medialit/models"; -import mongoose from "mongoose"; - -export default mongoose.models.Media || mongoose.model("Media", MediaSchema); diff --git a/apps/api/src/media/queries.ts b/apps/api/src/media/queries.ts index 68f7d0d6..d6b5f387 100644 --- a/apps/api/src/media/queries.ts +++ b/apps/api/src/media/queries.ts @@ -1,12 +1,18 @@ -import mongoose, { FilterQuery } from "mongoose"; import { numberOfRecordsPerPage } from "../config/constants"; import GetPageProps from "./GetPageProps"; -import MediaModel from "./model"; import { AccessControl, Constants, type MediaWithUserId, } from "@medialit/models"; +import { + countMedia, + createMediaRecord, + deleteMediaRecord, + getMediaRecord, + listMedia, + totalMediaSize, +} from "@/db"; export function buildMediaCountQuery({ userId, @@ -14,32 +20,20 @@ export function buildMediaCountQuery({ access, group, }: { - userId: string | mongoose.Types.ObjectId; + userId: string; apikey: string; access?: AccessControl; group?: string; -}): FilterQuery { - const query: FilterQuery = { - apikey, - userId, - temp: { $ne: true }, - }; - if (access) { - query.accessControl = - access === Constants.AccessControl.PRIVATE - ? Constants.AccessControl.PRIVATE - : Constants.AccessControl.PUBLIC; - } - if (typeof group === "string" && group.trim().length > 0) { - query.group = { $regex: `^${escapeRegex(group.trim())}` }; - } - return query; +}) { + return { userId: String(userId), apikey, access, group }; } function escapeRegex(value: string): string { return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } +export { escapeRegex }; + export async function getMedia({ userId, apikey, @@ -49,12 +43,8 @@ export async function getMedia({ apikey: string; mediaId: string; }): Promise { - return (await MediaModel.findOne({ - mediaId, - apikey, - userId, - // temp: { $ne: true }, - }).lean()) as MediaWithUserId | null; + const row = await getMediaRecord({ userId, apikey, mediaId }); + return row; } export async function getMediaCount({ @@ -68,43 +58,27 @@ export async function getMediaCount({ access?: AccessControl; group?: string; }): Promise { - const query = buildMediaCountQuery({ userId, apikey, access, group }); - return await MediaModel.countDocuments(query).lean(); + return countMedia({ + userId: String(userId), + apikey, + access: + access === Constants.AccessControl.PRIVATE + ? "private" + : access + ? "public" + : undefined, + group, + }); } export async function getTotalSpace({ userId, apikey, }: { - userId: mongoose.Types.ObjectId; + userId: string; apikey?: string; }): Promise { - const query = apikey - ? { userId, apikey, temp: { $ne: true } } - : { userId, temp: { $ne: true } }; - const result = await MediaModel.aggregate([ - { - $match: query, - }, - { - $group: { - _id: null, - totalSize: { $sum: "$size" }, - }, - }, - { - $project: { - _id: 0, - totalSize: 1, - }, - }, - ]); - - if (result.length === 0) { - return 0; - } - - return result[0].totalSize; + return totalMediaSize({ userId: String(userId), apikey }); } export async function getPaginatedMedia({ @@ -115,37 +89,45 @@ export async function getPaginatedMedia({ group, recordsPerPage, }: GetPageProps): Promise { - const query = buildMediaCountQuery({ userId, apikey, access, group }); - const limitWithFallback = recordsPerPage || numberOfRecordsPerPage; - - return await MediaModel.find(query, { - userId: 1, - mediaId: 1, - originalFileName: 1, - mimeType: 1, - size: 1, - accessControl: 1, - thumbnailGenerated: 1, - caption: 1, - group: 1, - }) - .sort({ _id: -1 }) - .skip(page ? (page - 1) * limitWithFallback : 0) - .limit(limitWithFallback); + return listMedia({ + userId: String(userId), + apikey, + access: access + ? access === Constants.AccessControl.PRIVATE + ? "private" + : "public" + : undefined, + group, + page, + recordsPerPage: recordsPerPage || numberOfRecordsPerPage, + }); } export async function deleteMediaQuery( userId: string, mediaId: string, -): Promise { - return await MediaModel.deleteOne({ userId, mediaId }); +): Promise { + await deleteMediaRecord(userId, mediaId); } export async function createMedia( mediaData: MediaWithUserId, ): Promise { - const media: MediaWithUserId = await MediaModel.create(mediaData); - return media; + return createMediaRecord({ + fileName: mediaData.fileName, + mediaId: mediaData.mediaId, + userId: String(mediaData.userId), + apikey: mediaData.apikey, + originalFileName: mediaData.originalFileName, + mimeType: mediaData.mimeType, + size: mediaData.size, + thumbnailGenerated: mediaData.thumbnailGenerated, + accessControl: + mediaData.accessControl === "public" ? "public" : "private", + group: mediaData.group, + caption: mediaData.caption, + temp: mediaData.temp ?? true, + }); } export default { diff --git a/apps/api/src/media/routes.ts b/apps/api/src/media/routes.ts index b86253e7..d101cb81 100644 --- a/apps/api/src/media/routes.ts +++ b/apps/api/src/media/routes.ts @@ -19,6 +19,8 @@ import signatureMiddleware from "../signature/middleware"; import storage from "./storage-middleware"; import { getSignatureFromReq } from "../signature/utils"; import { authenticatedApiLimiter } from "../auth/limiters"; +import { DATA_READ_SCOPE, DATA_WRITE_SCOPE } from "../auth/options"; +import { requireScope } from "../auth/scopes"; const router = express.Router(); @@ -92,7 +94,9 @@ router.post( next, ); } else { - apikey(req, res, next); + apikey(req, res, () => + requireScope(DATA_WRITE_SCOPE)(req, res, next), + ); } }, storage, @@ -120,6 +124,7 @@ router.post( */ authenticatedApiLimiter, apikey, + requireScope(DATA_READ_SCOPE), getMediaCount, ); router.post( @@ -141,6 +146,7 @@ router.post( */ authenticatedApiLimiter, apikey, + requireScope(DATA_READ_SCOPE), getTotalSpaceOccupied, ); router.post( @@ -170,6 +176,7 @@ router.post( */ authenticatedApiLimiter, apikey, + requireScope(DATA_READ_SCOPE), getMediaDetails, ); router.post( @@ -204,6 +211,7 @@ router.post( */ authenticatedApiLimiter, apikey, + requireScope(DATA_READ_SCOPE), getMedia, ); router.post( @@ -233,6 +241,7 @@ router.post( */ authenticatedApiLimiter, apikey, + requireScope(DATA_WRITE_SCOPE), sealMedia, ); router.delete( @@ -266,6 +275,7 @@ router.delete( */ authenticatedApiLimiter, apikey, + requireScope(DATA_WRITE_SCOPE), deleteMedia, ); diff --git a/apps/api/src/media/service.ts b/apps/api/src/media/service.ts index 280c8cf3..339eac3c 100644 --- a/apps/api/src/media/service.ts +++ b/apps/api/src/media/service.ts @@ -30,7 +30,6 @@ import logger from "../services/log"; import generateKey from "./utils/generate-key"; import { getMediaSettings } from "../media-settings/queries"; import generateFileName from "./utils/generate-file-name"; -import mongoose from "mongoose"; import GetPageProps from "./GetPageProps"; import { deleteMediaQuery, @@ -39,7 +38,7 @@ import { getPaginatedMedia, createMedia, } from "./queries"; -import MediaModel from "./model"; +import { sealMediaRecord } from "@/db"; import * as presignedUrlService from "../signature/service"; import getTags from "./utils/get-tags"; import { getPublicFileUrl, getThumbnailUrl } from "./utils/get-public-urls"; @@ -166,7 +165,7 @@ async function upload({ const mediaObject: MediaWithUserId = { fileName: `main.${fileExtension}`, mediaId: fileName.name, - userId: new mongoose.Types.ObjectId(userId), + userId, apikey, originalFileName: file.name, mimeType, @@ -481,10 +480,7 @@ async function sealMedia({ } // Update media record to remove temp flag - await MediaModel.updateOne( - { mediaId, userId, apikey }, - { $unset: { temp: "" } }, - ); + await sealMediaRecord({ mediaId, userId: String(userId), apikey }); // Fetch and return the updated media const updatedMedia = await getMedia({ userId, apikey, mediaId }); diff --git a/apps/api/src/media/storage-middleware.ts b/apps/api/src/media/storage-middleware.ts index e99e9b33..ff3bb407 100644 --- a/apps/api/src/media/storage-middleware.ts +++ b/apps/api/src/media/storage-middleware.ts @@ -1,9 +1,8 @@ -import { maxStorageAllowedNotSubscribed } from "../config/constants"; -import { maxStorageAllowedSubscribed } from "../config/constants"; -import { getSubscriptionStatus, User } from "@medialit/models"; +import { User } from "@medialit/models"; +import { maxStorageFor } from "../billing/entitlements"; import mediaQueries from "./queries"; import { FILE_SIZE_EXCEEDED, NOT_ENOUGH_STORAGE } from "../config/strings"; -import mongoose from "mongoose"; + import getMaxFileUploadSize from "./utils/get-max-file-upload-size"; export type UploadValidationResult = @@ -45,7 +44,7 @@ export async function validateUploadConstraints({ user, }: { size: number; - user: User & { _id: mongoose.Types.ObjectId }; + user: User & { _id: string }; }): Promise { const allowedFileSize = getMaxFileUploadSize({ user }); if (size > allowedFileSize) { @@ -70,14 +69,12 @@ export async function validateUploadConstraints({ export async function hasEnoughStorage( size: number, - user: User & { _id: mongoose.Types.ObjectId }, + user: User & { _id: string }, ): Promise { const totalSpaceOccupied = await mediaQueries.getTotalSpace({ userId: user._id, }); - const maxStorageAllowed = getSubscriptionStatus(user) - ? maxStorageAllowedSubscribed - : maxStorageAllowedNotSubscribed; + const maxStorageAllowed = maxStorageFor(user); return totalSpaceOccupied + size <= maxStorageAllowed; } diff --git a/apps/api/src/media/utils/get-max-file-upload-size.ts b/apps/api/src/media/utils/get-max-file-upload-size.ts index 0f175ad2..c85ea438 100644 --- a/apps/api/src/media/utils/get-max-file-upload-size.ts +++ b/apps/api/src/media/utils/get-max-file-upload-size.ts @@ -1,11 +1,5 @@ -import { - maxFileUploadSizeNotSubscribed, - maxFileUploadSizeSubscribed, -} from "../../config/constants"; -import { getSubscriptionStatus } from "@medialit/models"; +import { maxUploadFor } from "../../billing/entitlements"; export default function getMaxFileUploadSize(req: any): number { - return getSubscriptionStatus(req.user) - ? maxFileUploadSizeSubscribed - : maxFileUploadSizeNotSubscribed; + return maxUploadFor(req.user); } diff --git a/apps/api/src/oauth/__tests__/jwt.test.ts b/apps/api/src/oauth/__tests__/jwt.test.ts deleted file mode 100644 index f9cba442..00000000 --- a/apps/api/src/oauth/__tests__/jwt.test.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { test } from "node:test"; -import assert from "node:assert/strict"; -import crypto from "crypto"; -import jwt from "jsonwebtoken"; - -const TEST_KEY = crypto.randomBytes(32).toString("hex"); -process.env["OAUTH_SIGNING_KEY"] = TEST_KEY; - -let signAccessToken: any, - signRefreshToken: any, - verifyAccessToken: any, - verifyRefreshToken: any; -(async () => { - const mod = await import("../jwt.js"); - signAccessToken = mod.signAccessToken; - signRefreshToken = mod.signRefreshToken; - verifyAccessToken = mod.verifyAccessToken; - verifyRefreshToken = mod.verifyRefreshToken; -})(); - -async function waitForImport() { - while (!signAccessToken) await new Promise((r) => setTimeout(r, 10)); -} - -test("signAccessToken -> verifyAccessToken round trip", async () => { - await waitForImport(); - const token = signAccessToken("user-1", "client-1", ["read", "write"]); - const payload = verifyAccessToken(token); - assert.equal(payload?.sub, "user-1"); - assert.equal(payload?.cid, "client-1"); - assert.deepEqual(payload?.scope, ["read", "write"]); - assert.equal(typeof payload?.exp, "number"); -}); - -test("access token stores scope as an OAuth space-delimited string", async () => { - await waitForImport(); - const token = signAccessToken("user-1", "client-1", ["read", "write"]); - const decoded = jwt.decode(token) as any; - - assert.equal(decoded.scope, "read write"); -}); - -test("verifyAccessToken accepts legacy array scope tokens", async () => { - await waitForImport(); - const token = jwt.sign( - { - sub: "user-1", - cid: "client-1", - typ: "access", - scope: ["read", "write"], - }, - TEST_KEY, - { algorithm: "HS256" }, - ); - - const payload = verifyAccessToken(token); - assert.deepEqual(payload?.scope, ["read", "write"]); -}); - -test("signRefreshToken -> verifyRefreshToken round trip (jti present)", async () => { - await waitForImport(); - const token = signRefreshToken("user-1", "client-1"); - const payload = verifyRefreshToken(token); - assert.equal(payload?.sub, "user-1"); - assert.equal(payload?.cid, "client-1"); - assert.ok(payload?.jti, "refresh tokens must have a jti"); - assert.equal(typeof payload?.exp, "number"); -}); - -test("access token rejected as refresh token", async () => { - await waitForImport(); - const access = signAccessToken("user-1", "client-1"); - assert.equal(verifyRefreshToken(access), null); -}); - -test("refresh token rejected as access token", async () => { - await waitForImport(); - const refresh = signRefreshToken("user-1", "client-1"); - assert.equal(verifyAccessToken(refresh), null); -}); - -test("token signed with a different key is rejected", async () => { - await waitForImport(); - const other = crypto.randomBytes(32).toString("hex"); - const fake = jwt.sign({ cid: "evil", typ: "access" }, other, { - algorithm: "HS256", - subject: "evil-user", - expiresIn: 60, - }); - assert.equal(verifyAccessToken(fake), null); -}); - -test("garbage token is rejected", async () => { - await waitForImport(); - assert.equal(verifyAccessToken("not-a-jwt"), null); - assert.equal(verifyAccessToken(""), null); -}); - -test("expired access token is rejected", async () => { - await waitForImport(); - const expired = jwt.sign({ cid: "client-1", typ: "access" }, TEST_KEY, { - algorithm: "HS256", - subject: "user-1", - expiresIn: -10, - }); - assert.equal(verifyAccessToken(expired), null); -}); diff --git a/apps/api/src/oauth/__tests__/model.test.ts b/apps/api/src/oauth/__tests__/model.test.ts deleted file mode 100644 index d4c3b45b..00000000 --- a/apps/api/src/oauth/__tests__/model.test.ts +++ /dev/null @@ -1,242 +0,0 @@ -import { test } from "node:test"; -import assert from "node:assert/strict"; -import crypto from "crypto"; - -process.env["OAUTH_SIGNING_KEY"] = crypto.randomBytes(32).toString("hex"); - -test("registerClient persists DCR clients through OauthClient", async () => { - const clientModel = (await import("../client-model.js")).default as any; - const originalCreate = clientModel.create; - let saved: any; - clientModel.create = async (client: any) => { - saved = client; - return client; - }; - - try { - const { registerClient } = await import("../model.js"); - const response = await registerClient({ - redirect_uris: ["http://127.0.0.1:33418/"], - grant_types: ["authorization_code", "refresh_token"], - token_endpoint_auth_method: "none", - client_name: "VS Code", - scope: "read write", - }); - - assert.equal(saved.clientId, response.client_id); - assert.deepEqual(saved.redirectUris, response.redirect_uris); - assert.deepEqual(saved.grantTypes, [ - "authorization_code", - "refresh_token", - ]); - assert.equal(saved.tokenEndpointAuthMethod, "none"); - assert.equal(saved.clientName, "VS Code"); - assert.equal(saved.scope, "read write"); - assert.equal(response.client_secret_expires_at, 0); - } finally { - clientModel.create = originalCreate; - } -}); - -test("registerClient rejects non-loopback http redirect URIs", async () => { - const clientModel = (await import("../client-model.js")).default as any; - const originalCreate = clientModel.create; - let createCalled = false; - clientModel.create = async () => { - createCalled = true; - }; - - try { - const { DcrValidationError, registerClient } = await import( - "../model.js" - ); - - await assert.rejects( - registerClient({ - redirect_uris: ["http://example.com/callback"], - }), - DcrValidationError, - ); - assert.equal(createCalled, false); - } finally { - clientModel.create = originalCreate; - } -}); - -test("registerClient rejects redirect URIs with fragments or credentials", async () => { - const { DcrValidationError, registerClient } = await import("../model.js"); - - await assert.rejects( - registerClient({ - redirect_uris: ["https://example.com/callback#fragment"], - }), - DcrValidationError, - ); - - await assert.rejects( - registerClient({ - redirect_uris: ["https://user@example.com/callback"], - }), - DcrValidationError, - ); -}); - -test("registerClient rejects unsupported DCR grants", async () => { - const { DcrValidationError, registerClient } = await import("../model.js"); - - await assert.rejects( - registerClient({ - redirect_uris: ["http://127.0.0.1:33418/"], - grant_types: ["client_credentials"], - }), - DcrValidationError, - ); -}); - -test("redirectUriMatchesRegistered requires exact redirect URI match", async () => { - const { redirectUriMatchesRegistered } = await import("../helpers.js"); - const registered = ["https://example.com/callback"]; - - assert.equal( - redirectUriMatchesRegistered( - "https://example.com/callback", - registered, - ), - true, - ); - assert.equal( - redirectUriMatchesRegistered( - "https://example.com/callback?next=/evil", - registered, - ), - false, - ); -}); - -test("hashOtp binds OTP hashes to the pending session", async () => { - const { hashOtp } = await import("../helpers.js"); - - assert.equal( - hashOtp("pending-1", "123456"), - hashOtp("pending-1", "123456"), - ); - assert.notEqual( - hashOtp("pending-1", "123456"), - hashOtp("pending-2", "123456"), - ); -}); - -test("oauthModel.getClient resolves DCR clients from OauthClient", async () => { - const clientModel = (await import("../client-model.js")).default as any; - const originalFindOne = clientModel.findOne; - clientModel.findOne = () => ({ - lean: async () => ({ - clientId: "client-1", - redirectUris: ["http://127.0.0.1:33418/"], - grantTypes: ["authorization_code", "refresh_token"], - }), - }); - - try { - const { oauthModel } = await import("../model.js"); - const client = (await oauthModel.getClient("client-1", "")) as any; - - assert.equal(client?.id, "client-1"); - assert.deepEqual(client?.redirectUris, ["http://127.0.0.1:33418/"]); - assert.deepEqual(client?.grants, [ - "authorization_code", - "refresh_token", - ]); - } finally { - clientModel.findOne = originalFindOne; - } -}); - -test("oauthModel.saveToken uses expiry dates supplied by oauth2-server", async () => { - const { oauthModel } = await import("../model.js"); - const accessTokenExpiresAt = new Date("2030-01-01T00:00:00.000Z"); - const refreshTokenExpiresAt = new Date("2030-02-01T00:00:00.000Z"); - - const savedToken = (await oauthModel.saveToken( - { - accessTokenExpiresAt, - refreshTokenExpiresAt, - scope: ["read"], - } as any, - { id: "client-1" } as any, - { id: "user-1" } as any, - )) as any; - - assert.equal(savedToken.accessTokenExpiresAt, accessTokenExpiresAt); - assert.equal(savedToken.refreshTokenExpiresAt, refreshTokenExpiresAt); -}); - -test("oauthModel.getRefreshToken rejects persisted revoked refresh tokens", async () => { - const revokedTokenModel = (await import("../revoked-token-model.js")) - .default as any; - const originalFindOne = revokedTokenModel.findOne; - revokedTokenModel.findOne = () => ({ - lean: async () => ({ jti: "revoked-jti" }), - }); - - try { - const { oauthModel } = await import("../model.js"); - const savedToken = (await oauthModel.saveToken( - { scope: ["read"] } as any, - { id: "client-1" } as any, - { id: "user-1" } as any, - )) as any; - - const refreshToken = await oauthModel.getRefreshToken( - savedToken.refreshToken, - ); - - assert.equal(refreshToken, null); - } finally { - revokedTokenModel.findOne = originalFindOne; - } -}); - -test("oauthModel.revokeToken persists refresh token jti with expiry", async () => { - const revokedTokenModel = (await import("../revoked-token-model.js")) - .default as any; - const originalFindOne = revokedTokenModel.findOne; - const originalUpdateOne = revokedTokenModel.updateOne; - let update: any; - revokedTokenModel.findOne = () => ({ - lean: async () => null, - }); - revokedTokenModel.updateOne = async ( - filter: any, - payload: any, - options: any, - ) => { - update = { filter, payload, options }; - return { acknowledged: true, upsertedCount: 1 }; - }; - - try { - const { oauthModel } = await import("../model.js"); - const savedToken = (await oauthModel.saveToken( - { scope: ["read"] } as any, - { id: "client-1" } as any, - { id: "user-1" } as any, - )) as any; - const refreshToken = await oauthModel.getRefreshToken( - savedToken.refreshToken, - ); - - assert.ok(refreshToken); - await oauthModel.revokeToken(refreshToken); - - assert.equal(update.filter.jti, update.payload.$setOnInsert.jti); - assert.equal(update.payload.$setOnInsert.tokenType, "refresh_token"); - assert.equal(update.payload.$setOnInsert.userId, "user-1"); - assert.equal(update.payload.$setOnInsert.clientId, "client-1"); - assert.ok(update.payload.$setOnInsert.expiresAt instanceof Date); - assert.deepEqual(update.options, { upsert: true }); - } finally { - revokedTokenModel.findOne = originalFindOne; - revokedTokenModel.updateOne = originalUpdateOne; - } -}); diff --git a/apps/api/src/oauth/__tests__/rate-limit.test.ts b/apps/api/src/oauth/__tests__/rate-limit.test.ts deleted file mode 100644 index dbc48a41..00000000 --- a/apps/api/src/oauth/__tests__/rate-limit.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { test } from "node:test"; -import assert from "node:assert/strict"; -import rateLimit from "express-rate-limit"; - -test("rate limiters are valid express middleware functions", () => { - const limiter = rateLimit({ windowMs: 60_000, max: 10 }); - assert.equal(typeof limiter, "function"); - assert.equal(limiter.length, 3); -}); - -test("separate limiters have independent state", () => { - const a = rateLimit({ windowMs: 60_000, max: 10 }); - const b = rateLimit({ windowMs: 60_000, max: 30 }); - assert.notEqual(a, b); -}); diff --git a/apps/api/src/oauth/authorize-page.ts b/apps/api/src/oauth/authorize-page.ts deleted file mode 100644 index de499ce0..00000000 --- a/apps/api/src/oauth/authorize-page.ts +++ /dev/null @@ -1,339 +0,0 @@ -import { Response as ExpressRes } from "express"; - -export function authorizePage(pendingId: string, clientId: string): string { - return ` - - - - -Authorize MediaLit - - - -
- -
-
-
M
- MediaLit -
- -
-
- "Instead of rebuilding file uploads and processing pipelines for every project, I just plug in MediaLit. Now my apps, my agents, and I can all work seamlessly on the same set of files." -
- -
-
R
-
-
Rajat
-
Software Engineer
-
-
-
- - -
- - -
-
-
-

Get started

-

Enter your email to sign in or create an account

- -
- - -
-
- - -
- -
-

Verify OTP

-

Enter the code sent to

- -
- - -
-
- - -
- - -
-
-
- - - -`; -} - -export function errorPage(res: ExpressRes, msg: string) { - res.type("html").status(400).send(` -Error - -

Authorization Error

${msg}

`); -} diff --git a/apps/api/src/oauth/client-model.ts b/apps/api/src/oauth/client-model.ts deleted file mode 100644 index eaf29306..00000000 --- a/apps/api/src/oauth/client-model.ts +++ /dev/null @@ -1,5 +0,0 @@ -import mongoose from "mongoose"; -import { OauthClientSchema } from "@medialit/models"; - -export default mongoose.models.OauthClient || - mongoose.model("OauthClient", OauthClientSchema); diff --git a/apps/api/src/oauth/helpers.ts b/apps/api/src/oauth/helpers.ts deleted file mode 100644 index 9a17f964..00000000 --- a/apps/api/src/oauth/helpers.ts +++ /dev/null @@ -1,43 +0,0 @@ -import crypto from "crypto"; -import { Request as ExpressReq } from "express"; - -export const OTP_TTL_MS = 5 * 60 * 1000; -export const PENDING_AUTH_TTL_MS = 10 * 60 * 1000; -export const OTP_RESEND_COOLDOWN_MS = 60 * 1000; -export const MAX_OTP_ATTEMPTS = 5; - -export function generateOtp(): string { - return String(Math.floor(100000 + crypto.randomInt(0, 900000))); -} - -export function hashOtp(pendingId: string, otp: string): string { - return crypto - .createHmac("sha256", process.env.OAUTH_SIGNING_KEY || "") - .update(`${pendingId}:${otp}`) - .digest("hex"); -} - -export function generatePendingId(): string { - return crypto.randomBytes(16).toString("hex"); -} - -export function singleQueryParam(val: unknown): string | undefined { - if (Array.isArray(val)) return val.length > 0 ? String(val[0]) : undefined; - if (val === undefined || val === null) return undefined; - return String(val); -} - -export function redirectUriMatchesRegistered( - redirectUri: string, - registeredUris: string[], -): boolean { - return registeredUris.some((uri) => redirectUri === uri); -} - -export function reqHeadersHost(req: ExpressReq): string | null { - const forwarded = req.headers["x-forwarded-host"]; - if (forwarded) return Array.isArray(forwarded) ? forwarded[0] : forwarded; - const host = req.headers.host; - if (host) return Array.isArray(host) ? host[0] : host; - return null; -} diff --git a/apps/api/src/oauth/jwt.ts b/apps/api/src/oauth/jwt.ts deleted file mode 100644 index 1bb393b7..00000000 --- a/apps/api/src/oauth/jwt.ts +++ /dev/null @@ -1,89 +0,0 @@ -import jwt from "jsonwebtoken"; -import { randomUUID } from "crypto"; - -const KEYS = (process.env.OAUTH_SIGNING_KEY || "") - .split(",") - .map((s) => s.trim()) - .filter(Boolean); - -const SIGNING_KEY = KEYS[0]; -const VERIFY_KEYS = KEYS; - -const ACCESS_TOKEN_TTL = Number(process.env.TOKEN_TTL_SECONDS) || 900; -const REFRESH_TOKEN_TTL = 60 * 60 * 24 * 30; // 30 days - -export function signAccessToken( - userId: string, - clientId: string, - scope: string[] = [], -): string { - return jwt.sign( - { sub: userId, cid: clientId, typ: "access", scope: scope.join(" ") }, - SIGNING_KEY, - { algorithm: "HS256", expiresIn: ACCESS_TOKEN_TTL, noTimestamp: false }, - ); -} - -export function signRefreshToken(userId: string, clientId: string): string { - return jwt.sign( - { - sub: userId, - cid: clientId, - typ: "refresh", - jti: randomUUID(), - }, - SIGNING_KEY, - { algorithm: "HS256", expiresIn: REFRESH_TOKEN_TTL }, - ); -} - -export function verifyToken( - token: string, - expectedType: "access" | "refresh", -): { - sub: string; - cid: string; - scope: string[]; - exp?: number; - jti?: string; -} | null { - for (const key of VERIFY_KEYS) { - try { - const decoded = jwt.verify(token, key, { - algorithms: ["HS256"], - }) as any; - if (decoded.typ !== expectedType) return null; - return { - sub: decoded.sub, - cid: decoded.cid, - scope: normalizeScope(decoded.scope), - exp: decoded.exp, - jti: decoded.jti, - }; - } catch { - continue; - } - } - return null; -} - -export function verifyAccessToken(token: string) { - return verifyToken(token, "access"); -} - -export function verifyRefreshToken(token: string) { - return verifyToken(token, "refresh"); -} - -function normalizeScope(scope: unknown): string[] { - if (Array.isArray(scope)) { - return scope.filter((item): item is string => typeof item === "string"); - } - if (typeof scope === "string") { - return scope.split(/\s+/).filter(Boolean); - } - return []; -} - -export const ACCESS_TOKEN_TTL_SECONDS = ACCESS_TOKEN_TTL; -export const REFRESH_TOKEN_TTL_SECONDS = REFRESH_TOKEN_TTL; diff --git a/apps/api/src/oauth/limiters.ts b/apps/api/src/oauth/limiters.ts deleted file mode 100644 index c8978956..00000000 --- a/apps/api/src/oauth/limiters.ts +++ /dev/null @@ -1,67 +0,0 @@ -import rateLimit from "express-rate-limit"; - -export const authorizeLimiter = rateLimit({ - windowMs: 60_000, - max: 10, - standardHeaders: true, - legacyHeaders: false, - message: { - error: "too_many_requests", - error_description: "Too many requests, please try again later.", - }, -}); - -export const verifyOtpLimiter = rateLimit({ - windowMs: 60_000, - max: 10, - standardHeaders: true, - legacyHeaders: false, - message: { - error: "too_many_requests", - error_description: "Too many requests, please try again later.", - }, -}); - -export const tokenLimiter = rateLimit({ - windowMs: 60_000, - max: 30, - standardHeaders: true, - legacyHeaders: false, - message: { - error: "too_many_requests", - error_description: "Too many requests, please try again later.", - }, -}); - -export const revokeLimiter = rateLimit({ - windowMs: 60_000, - max: 30, - standardHeaders: true, - legacyHeaders: false, - message: { - error: "too_many_requests", - error_description: "Too many requests.", - }, -}); - -export const userinfoLimiter = rateLimit({ - windowMs: 60_000, - max: 30, - standardHeaders: true, - legacyHeaders: false, - message: { - error: "too_many_requests", - error_description: "Too many requests.", - }, -}); - -export const registerLimiter = rateLimit({ - windowMs: 60_000, - max: 10, - standardHeaders: true, - legacyHeaders: false, - message: { - error: "too_many_requests", - error_description: "Too many client registration requests.", - }, -}); diff --git a/apps/api/src/oauth/middleware.ts b/apps/api/src/oauth/middleware.ts deleted file mode 100644 index e2b90453..00000000 --- a/apps/api/src/oauth/middleware.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { verifyAccessToken } from "./jwt"; - -export async function validateBearerToken( - bearer: string, -): Promise<{ userId: string; clientId: string; scopes: string[] } | null> { - const payload = verifyAccessToken(bearer); - if (!payload) return null; - return { - userId: payload.sub, - clientId: payload.cid, - scopes: payload.scope, - }; -} diff --git a/apps/api/src/oauth/model.ts b/apps/api/src/oauth/model.ts deleted file mode 100644 index 3c71d544..00000000 --- a/apps/api/src/oauth/model.ts +++ /dev/null @@ -1,387 +0,0 @@ -import crypto from "crypto"; -import type OAuth2Server from "@node-oauth/oauth2-server"; -import logger from "../services/log"; -import OauthClient from "./client-model"; -import OauthRevokedToken from "./revoked-token-model"; -import { - signAccessToken, - signRefreshToken, - verifyAccessToken, - verifyRefreshToken, - ACCESS_TOKEN_TTL_SECONDS, - REFRESH_TOKEN_TTL_SECONDS, -} from "./jwt"; - -interface StoredAuthorizationCode { - authorizationCode: string; - expiresAt: Date; - redirectUri: string; - scope?: string[]; - client: OAuth2Server.Client; - user: OAuth2Server.User; - codeChallenge?: string; - codeChallengeMethod?: string; -} - -interface StoredAccessToken { - accessToken: string; - accessTokenExpiresAt?: Date; - scope?: string[]; - client: OAuth2Server.Client; - user: OAuth2Server.User; -} - -interface StoredRefreshToken { - refreshToken: string; - refreshTokenExpiresAt?: Date; - scope?: string[]; - client: OAuth2Server.Client; - user: OAuth2Server.User; -} - -const authorizationCodes = new Map(); - -const authorizationCodeSweep = setInterval( - () => { - const now = new Date(); - authorizationCodes.forEach((data, code) => { - if (data.expiresAt < now) authorizationCodes.delete(code); - }); - }, - 5 * 60 * 1000, -); -authorizationCodeSweep.unref?.(); - -const STATIC_CLIENTS: Record = { - "web-client": { - redirectUris: [ - `${process.env.WEB_CLIENT || "http://localhost:3000"}/api/auth/callback/medialit`, - ].filter((uri): uri is string => !!uri), - }, - "mobile-app": { - redirectUris: ["medialit://oauth/callback"], - }, -}; - -interface DynamicClient { - clientId: string; - clientIdIssuedAt: number; - redirectUris: string[]; - grantTypes: string[]; - tokenEndpointAuthMethod: string; - clientName?: string; - scope?: string; -} - -export class DcrValidationError extends Error { - constructor(message: string) { - super(message); - this.name = "DcrValidationError"; - } -} - -const MAX_REDIRECT_URIS = 10; -const MAX_REDIRECT_URI_LENGTH = 2048; -const ALLOWED_DCR_GRANTS = ["authorization_code", "refresh_token"]; - -function isLoopbackRedirect(url: URL): boolean { - return ( - url.hostname === "localhost" || - url.hostname === "127.0.0.1" || - url.hostname === "[::1]" || - url.hostname === "::1" - ); -} - -function validateRedirectUri(uri: unknown): string { - if (typeof uri !== "string") { - throw new DcrValidationError("redirect_uri must be a string"); - } - - if (uri.length > MAX_REDIRECT_URI_LENGTH) { - throw new DcrValidationError("redirect_uri is too long"); - } - - let parsed: URL; - try { - parsed = new URL(uri); - } catch { - throw new DcrValidationError("redirect_uri must be a valid URL"); - } - - if (parsed.hash) { - throw new DcrValidationError( - "redirect_uri must not contain a fragment", - ); - } - - if (parsed.username || parsed.password) { - throw new DcrValidationError( - "redirect_uri must not contain credentials", - ); - } - - if (parsed.protocol !== "https:") { - if (parsed.protocol !== "http:" || !isLoopbackRedirect(parsed)) { - throw new DcrValidationError( - "redirect_uri must use HTTPS unless it is localhost or loopback", - ); - } - } - - return parsed.toString(); -} - -function validateGrantTypes(grantTypes?: unknown): string[] { - const grants = grantTypes ?? ["authorization_code"]; - if (!Array.isArray(grants) || grants.length === 0) { - throw new DcrValidationError("grant_types must be a non-empty array"); - } - const invalid = grants.find((grant) => !ALLOWED_DCR_GRANTS.includes(grant)); - if (invalid) { - throw new DcrValidationError(`Unsupported grant_type: ${invalid}`); - } - return grants; -} - -function validateTokenEndpointAuthMethod(method: string): "none" { - if (method !== "none") { - throw new DcrValidationError("token_endpoint_auth_method must be none"); - } - return "none"; -} - -function sanitizeDcrClient(client: DynamicClient): DynamicClient { - if (!Array.isArray(client.redirectUris)) { - throw new DcrValidationError("redirect_uris must be an array"); - } - - if (client.redirectUris.length === 0) { - throw new DcrValidationError("At least one redirect_uri is required"); - } - - if (client.redirectUris.length > MAX_REDIRECT_URIS) { - throw new DcrValidationError("Too many redirect_uris"); - } - - return { - clientId: client.clientId, - clientIdIssuedAt: client.clientIdIssuedAt, - redirectUris: client.redirectUris.map(validateRedirectUri), - grantTypes: validateGrantTypes(client.grantTypes), - tokenEndpointAuthMethod: validateTokenEndpointAuthMethod( - client.tokenEndpointAuthMethod, - ), - clientName: client.clientName, - scope: client.scope, - }; -} - -export interface DcrRequest { - redirect_uris: string[]; - grant_types?: string[]; - token_endpoint_auth_method?: string; - client_name?: string; - scope?: string; -} - -export interface DcrResponse { - client_id: string; - client_id_issued_at: number; - client_secret_expires_at: number; - redirect_uris: string[]; - grant_types: string[]; - token_endpoint_auth_method: string; - client_name?: string; - scope?: string; -} - -export async function registerClient(meta: DcrRequest): Promise { - const clientId = crypto.randomUUID(); - const now = Math.floor(Date.now() / 1000); - const client: DynamicClient = { - clientId, - clientIdIssuedAt: now, - redirectUris: meta.redirect_uris, - grantTypes: meta.grant_types ?? ["authorization_code"], - tokenEndpointAuthMethod: meta.token_endpoint_auth_method ?? "none", - clientName: meta.client_name, - scope: meta.scope, - }; - const sanitized = sanitizeDcrClient(client); - await OauthClient.create(sanitized); - return { - client_id: sanitized.clientId, - client_id_issued_at: sanitized.clientIdIssuedAt, - client_secret_expires_at: 0, - redirect_uris: sanitized.redirectUris, - grant_types: sanitized.grantTypes, - token_endpoint_auth_method: sanitized.tokenEndpointAuthMethod, - client_name: sanitized.clientName, - scope: sanitized.scope, - }; -} - -export const oauthModel: OAuth2Server.AuthorizationCodeModel & - OAuth2Server.RefreshTokenModel = { - async getClient(clientId: string, _clientSecret?: string) { - const dyn = (await OauthClient.findOne({ - clientId, - }).lean()) as DynamicClient | null; - if (dyn) { - return { - id: dyn.clientId, - redirectUris: dyn.redirectUris, - grants: dyn.grantTypes, - accessTokenLifetime: ACCESS_TOKEN_TTL_SECONDS, - refreshTokenLifetime: 60 * 60 * 24 * 30, - }; - } - const stat = STATIC_CLIENTS[clientId]; - if (stat) { - return { - id: clientId, - redirectUris: stat.redirectUris, - grants: ["authorization_code", "refresh_token"], - accessTokenLifetime: ACCESS_TOKEN_TTL_SECONDS, - refreshTokenLifetime: 60 * 60 * 24 * 30, // 30 days - }; - } - logger.warn({ clientId }, "Unknown client_id rejected"); - return null; - }, - - async saveAuthorizationCode( - code: Pick< - StoredAuthorizationCode, - | "authorizationCode" - | "expiresAt" - | "redirectUri" - | "scope" - | "codeChallenge" - | "codeChallengeMethod" - >, - client: OAuth2Server.Client, - user: OAuth2Server.User, - ): Promise { - const stored: StoredAuthorizationCode = { - authorizationCode: code.authorizationCode, - expiresAt: code.expiresAt, - redirectUri: code.redirectUri, - scope: code.scope, - client, - user, - codeChallenge: code.codeChallenge, - codeChallengeMethod: code.codeChallengeMethod, - }; - authorizationCodes.set(code.authorizationCode, stored); - return stored; - }, - - async getAuthorizationCode( - authorizationCode: string, - ): Promise { - const stored = authorizationCodes.get(authorizationCode); - if (!stored) return null; - return stored; - }, - - async revokeAuthorizationCode( - code: StoredAuthorizationCode, - ): Promise { - authorizationCodes.delete(code.authorizationCode); - return true; - }, - - async saveToken( - token: Partial, - client: OAuth2Server.Client, - user: OAuth2Server.User, - ): Promise { - const userId = String((user as any).id); - const clientId = String((client as any).id); - const scope = token.scope; - - const accessToken = signAccessToken(userId, clientId, scope); - const refreshToken = signRefreshToken(userId, clientId); - - return { - accessToken, - accessTokenExpiresAt: token.accessTokenExpiresAt, - refreshToken, - refreshTokenExpiresAt: token.refreshTokenExpiresAt, - scope, - client, - user, - custom: { userId }, - }; - }, - - async getAccessToken( - accessToken: string, - ): Promise { - const payload = verifyAccessToken(accessToken); - if (!payload) return null; - return { - accessToken, - accessTokenExpiresAt: payload.exp - ? new Date(payload.exp * 1000) - : new Date(Date.now() + ACCESS_TOKEN_TTL_SECONDS * 1000), - scope: payload.scope, - client: { id: payload.cid } as OAuth2Server.Client, - user: { id: payload.sub } as OAuth2Server.User, - }; - }, - - async getRefreshToken( - refreshToken: string, - ): Promise { - const payload = verifyRefreshToken(refreshToken); - if (!payload) return null; - if (payload.jti) { - const revoked = await OauthRevokedToken.findOne({ - jti: payload.jti, - }).lean(); - if (revoked) return null; - } - return { - refreshToken, - refreshTokenExpiresAt: payload.exp - ? new Date(payload.exp * 1000) - : new Date(Date.now() + REFRESH_TOKEN_TTL_SECONDS * 1000), - scope: payload.scope, - client: { id: payload.cid } as OAuth2Server.Client, - user: { id: payload.sub } as OAuth2Server.User, - }; - }, - - async revokeToken(token: StoredRefreshToken): Promise { - const payload = verifyRefreshToken(token.refreshToken); - if (payload?.jti) { - await OauthRevokedToken.updateOne( - { jti: payload.jti }, - { - $setOnInsert: { - jti: payload.jti, - tokenType: "refresh_token", - userId: payload.sub, - clientId: payload.cid, - expiresAt: payload.exp - ? new Date(payload.exp * 1000) - : (token.refreshTokenExpiresAt ?? - new Date( - Date.now() + REFRESH_TOKEN_TTL_SECONDS * 1000, - )), - revokedAt: new Date(), - }, - }, - { upsert: true }, - ); - } - return true; - }, - - async verifyScope(_token: OAuth2Server.Token, _scope: string[]) { - return true; - }, -}; diff --git a/apps/api/src/oauth/pending-auth-model.ts b/apps/api/src/oauth/pending-auth-model.ts deleted file mode 100644 index 4d0885e1..00000000 --- a/apps/api/src/oauth/pending-auth-model.ts +++ /dev/null @@ -1,5 +0,0 @@ -import mongoose from "mongoose"; -import { OauthPendingAuthSchema } from "@medialit/models"; - -export default mongoose.models.OauthPendingAuth || - mongoose.model("OauthPendingAuth", OauthPendingAuthSchema); diff --git a/apps/api/src/oauth/revoked-token-model.ts b/apps/api/src/oauth/revoked-token-model.ts deleted file mode 100644 index bea528f7..00000000 --- a/apps/api/src/oauth/revoked-token-model.ts +++ /dev/null @@ -1,5 +0,0 @@ -import mongoose from "mongoose"; -import { OauthRevokedTokenSchema } from "@medialit/models"; - -export default mongoose.models.OauthRevokedToken || - mongoose.model("OauthRevokedToken", OauthRevokedTokenSchema); diff --git a/apps/api/src/oauth/routes.ts b/apps/api/src/oauth/routes.ts deleted file mode 100644 index 09ca95a3..00000000 --- a/apps/api/src/oauth/routes.ts +++ /dev/null @@ -1,508 +0,0 @@ -import { Router, Request as ExpressReq, Response as ExpressRes } from "express"; -import { z } from "zod"; -import type { OauthPendingAuth as PendingAuthRecord } from "@medialit/models"; -import { DcrValidationError, oauthModel, registerClient } from "./model"; -import type { DcrRequest, DcrResponse } from "./model"; -import { OAuth2Server, oauth } from "./server"; -import { authorizePage, errorPage } from "./authorize-page"; -import { findByEmail, createUser, getUser } from "../user/queries"; -import { verifyAccessToken } from "./jwt"; -import logger from "../services/log"; -import OauthPendingAuth from "./pending-auth-model"; -import { - authorizeLimiter, - registerLimiter, - revokeLimiter, - tokenLimiter, - userinfoLimiter, - verifyOtpLimiter, -} from "./limiters"; -import { - generateOtp, - generatePendingId, - hashOtp, - MAX_OTP_ATTEMPTS, - OTP_TTL_MS, - OTP_RESEND_COOLDOWN_MS, - PENDING_AUTH_TTL_MS, - redirectUriMatchesRegistered, - reqHeadersHost, - singleQueryParam, -} from "./helpers"; - -export const oauthRouter = Router(); - -oauthRouter.get( - "/.well-known/oauth-authorization-server", - (_req: ExpressReq, res: ExpressRes) => { - const host = reqHeadersHost(_req) || "localhost:8000"; - const baseUrl = host.includes("localhost") - ? `http://${host}` - : `https://${host}`; - res.json({ - issuer: baseUrl, - authorization_endpoint: `${baseUrl}/oauth/authorize`, - token_endpoint: `${baseUrl}/oauth/token`, - revocation_endpoint: `${baseUrl}/oauth/revoke`, - registration_endpoint: `${baseUrl}/oauth/register`, - response_types_supported: ["code"], - grant_types_supported: ["authorization_code", "refresh_token"], - code_challenge_methods_supported: ["S256"], - token_endpoint_auth_methods_supported: ["none"], - }); - }, -); - -oauthRouter.get( - "/oauth/authorize", - authorizeLimiter, - async (req: ExpressReq, res: ExpressRes) => { - try { - const parsed = z - .object({ - response_type: z.literal("code"), - client_id: z.string().min(1, "Missing client_id."), - redirect_uri: z.string().min(1, "Missing redirect_uri."), - code_challenge: z - .string() - .min(1, "Missing code_challenge (PKCE required)."), - code_challenge_method: z.string().optional(), - state: z.string().optional(), - scope: z.string().optional(), - }) - .safeParse({ - response_type: singleQueryParam(req.query.response_type), - client_id: singleQueryParam(req.query.client_id), - redirect_uri: singleQueryParam(req.query.redirect_uri), - code_challenge: singleQueryParam(req.query.code_challenge), - code_challenge_method: singleQueryParam( - req.query.code_challenge_method, - ), - state: singleQueryParam(req.query.state), - scope: singleQueryParam(req.query.scope), - }); - - if (!parsed.success) { - return errorPage(res, parsed.error.errors[0].message); - } - - const q = parsed.data; - - const client = await oauthModel.getClient(q.client_id, ""); - if (!client) { - return errorPage(res, "Invalid client_id."); - } - - const uris = client.redirectUris as string[] | undefined; - if (uris && uris.length > 0) { - const matched = redirectUriMatchesRegistered( - q.redirect_uri, - uris, - ); - if (!matched) { - return errorPage( - res, - "redirect_uri does not match registered client.", - ); - } - } - - const pendingId = generatePendingId(); - await OauthPendingAuth.create({ - clientId: q.client_id, - redirectUri: q.redirect_uri, - codeChallenge: q.code_challenge, - codeChallengeMethod: q.code_challenge_method, - state: q.state, - scope: q.scope, - pendingId, - expiresAt: new Date(Date.now() + PENDING_AUTH_TTL_MS), - }); - - res.type("html").send(authorizePage(pendingId, q.client_id)); - } catch (err: any) { - logger.error({ error: err.message }, "OAuth authorize page error"); - errorPage(res, "An error occurred. Please try again."); - } - }, -); - -oauthRouter.post( - "/oauth/authorize/send-otp", - verifyOtpLimiter, - async (req: ExpressReq, res: ExpressRes) => { - try { - const { pendingId, email } = req.body || {}; - if (!pendingId || !email) { - return res.json({ - success: false, - error: "Missing pendingId or email", - }); - } - if (!/^[0-9a-f]{32}$/.test(String(pendingId))) { - return res.json({ success: false, error: "Invalid request" }); - } - if ( - !/^[^\s@]+@[^\s@.]+\.[^\s@]+$/.test(String(email).slice(0, 320)) - ) { - return res.json({ - success: false, - error: "Invalid email address", - }); - } - - const pending = (await OauthPendingAuth.findOne({ - pendingId: String(pendingId), - expiresAt: { $gt: new Date() }, - }).lean()) as PendingAuthRecord | null; - if (!pending) { - return res.json({ - success: false, - error: "Authorization session expired. Please go back and try again.", - }); - } - - if ( - pending.otpSentAt && - Date.now() - pending.otpSentAt.getTime() < - OTP_RESEND_COOLDOWN_MS - ) { - return res.json({ - success: false, - error: "Please wait before requesting another code.", - }); - } - - const otp = generateOtp(); - const emailValue = String(email); - await OauthPendingAuth.updateOne( - { pendingId: String(pendingId) }, - { - $set: { - email: emailValue, - otpHash: hashOtp(String(pendingId), otp), - otpExpires: new Date(Date.now() + OTP_TTL_MS), - otpSentAt: new Date(), - otpAttempts: 0, - }, - }, - ); - - if (process.env.NODE_ENV !== "production") { - logger.info({ email: emailValue, otp }, "[Dev] OTP generated"); - return res.json({ success: true, otp }); - } else { - try { - const nodemailer = require("nodemailer"); - if (process.env.EMAIL_HOST && process.env.EMAIL_USER) { - const transporter = nodemailer.createTransport({ - host: process.env.EMAIL_HOST, - port: Number(process.env.EMAIL_PORT) || 587, - auth: { - user: process.env.EMAIL_USER, - pass: process.env.EMAIL_PASS, - }, - }); - await transporter.sendMail({ - from: process.env.EMAIL_FROM, - to: email, - subject: "Your MediaLit verification code", - text: `Enter this code to authorize: ${otp}`, - html: `

Enter this code to authorize MediaLit access:

${otp}

`, - }); - } - } catch (mailErr: any) { - logger.error( - { email, error: mailErr.message }, - "Failed to send OTP email", - ); - } - } - - res.json({ success: true }); - } catch (err: any) { - logger.error({ error: err.message }, "Send OTP error"); - res.json({ - success: false, - error: "Failed to send verification code", - }); - } - }, -); - -oauthRouter.post( - "/oauth/authorize/verify-otp", - verifyOtpLimiter, - async (req: ExpressReq, res: ExpressRes) => { - try { - const parsed = z - .object({ - pendingId: z - .string() - .regex(/^[0-9a-f]{32}$/, "Invalid request"), - otp: z.string().regex(/^\d{6}$/, "Invalid code format"), - }) - .safeParse(req.body || {}); - - if (!parsed.success) { - return res.json({ - success: false, - error: parsed.error.errors[0].message, - }); - } - - const { pendingId, otp } = parsed.data; - - const pending = (await OauthPendingAuth.findOneAndUpdate( - { - pendingId, - expiresAt: { $gt: new Date() }, - }, - { $inc: { otpAttempts: 1 } }, - { new: true }, - ).lean()) as PendingAuthRecord | null; - if (!pending) { - return res.json({ - success: false, - error: "Session expired. Please restart authorization.", - }); - } - - if ((pending.otpAttempts || 0) > MAX_OTP_ATTEMPTS) { - await OauthPendingAuth.deleteOne({ pendingId }); - return res.json({ - success: false, - error: "Too many attempts. Please restart authorization.", - }); - } - - if ( - !pending.otpHash || - !pending.otpExpires || - pending.otpExpires < new Date() - ) { - await OauthPendingAuth.deleteOne({ pendingId }); - return res.json({ - success: false, - error: "Code expired. Please restart authorization.", - }); - } - - if (pending.otpHash !== hashOtp(pendingId, String(otp))) { - return res.json({ success: false, error: "Invalid code." }); - } - - const email = pending.email!; - let user = await findByEmail(email); - if (!user) { - user = await createUser(email, undefined, "subscribed"); - } - const userId = String((user as any)._id || (user as any).id); - - const oauthReq = new OAuth2Server.Request({ - headers: { - "content-type": "application/x-www-form-urlencoded", - ...Object.fromEntries( - Object.entries(req.headers).map(([k, v]) => [ - k, - String(v), - ]), - ), - }, - method: "POST", - query: {} as Record, - body: { - response_type: "code", - client_id: pending.clientId, - redirect_uri: pending.redirectUri, - scope: pending.scope, - state: pending.state, - code_challenge: pending.codeChallenge, - code_challenge_method: pending.codeChallengeMethod, - }, - }); - - const oauthRes = new OAuth2Server.Response({}); - - await oauth.authorize(oauthReq, oauthRes, { - authenticateHandler: { - handle: () => Promise.resolve({ id: userId, email }), - }, - }); - - const location = oauthRes.get("Location"); - if (!location) { - throw new Error( - "No redirect URI returned from authorize handler", - ); - } - - await OauthPendingAuth.deleteOne({ pendingId }); - - res.json({ success: true, redirectUri: location }); - } catch (err: any) { - logger.error({ error: err.message }, "Verify OTP error"); - res.json({ - success: false, - error: "Verification failed. Please try again.", - }); - } - }, -); - -oauthRouter.post( - "/oauth/token", - tokenLimiter, - async (req: ExpressReq, res: ExpressRes) => { - try { - const oauthReq = new OAuth2Server.Request({ - headers: { - "content-type": - req.headers["content-type"] || - "application/x-www-form-urlencoded", - ...Object.fromEntries( - Object.entries(req.headers).map(([k, v]) => [ - k, - String(v), - ]), - ), - }, - method: "POST", - query: {} as Record, - body: req.body || {}, - }); - - const oauthRes = new OAuth2Server.Response({}); - - await oauth.token(oauthReq, oauthRes); - - const body = (oauthRes as any).body; - if (!body) { - throw new Error("No response from token handler"); - } - - res.set(oauthRes.headers || {}); - res.status(oauthRes.status || 200).json(body); - } catch (err: any) { - logger.error({ error: err.message }, "Token exchange error"); - if (err instanceof OAuth2Server.OAuthError) { - res.status(err.code || 400).json({ - error: err.name, - error_description: err.message, - }); - } else { - res.status(500).json({ - error: "server_error", - error_description: "An unexpected error occurred.", - }); - } - } - }, -); - -oauthRouter.get( - "/oauth/userinfo", - userinfoLimiter, - async (req: ExpressReq, res: ExpressRes) => { - try { - const authParsed = z - .object({ - authorization: z - .string() - .regex( - /^Bearer\s+.+$/, - "Missing or invalid authorization header.", - ), - }) - .safeParse({ authorization: req.headers.authorization }); - - if (!authParsed.success) { - return res.status(401).json({ - error: "invalid_token", - error_description: authParsed.error.errors[0].message, - }); - } - const token = authParsed.data.authorization.substring(7); - const payload = verifyAccessToken(token); - if (!payload) { - return res.status(401).json({ - error: "invalid_token", - error_description: "Access token is invalid or expired.", - }); - } - const user = await getUser(payload.sub); - if (!user) { - return res.status(401).json({ - error: "invalid_token", - error_description: "User not found.", - }); - } - res.json({ - sub: String(user._id || user.id), - email: user.email, - name: user.name || "", - }); - } catch (err: any) { - logger.error({ error: err.message }, "UserInfo endpoint error"); - res.status(500).json({ - error: "server_error", - error_description: "An unexpected error occurred.", - }); - } - }, -); - -oauthRouter.post( - "/oauth/revoke", - revokeLimiter, - async (req: ExpressReq, res: ExpressRes) => { - try { - const { token } = req.body || {}; - if (token) { - const refreshToken = await oauthModel.getRefreshToken(token); - if (refreshToken) { - await oauthModel.revokeToken(refreshToken); - } - } - res.status(200).json({}); - } catch { - res.status(200).json({}); - } - }, -); - -oauthRouter.post( - "/oauth/register", - registerLimiter, - async (req: ExpressReq, res: ExpressRes) => { - try { - const meta = req.body as DcrRequest; - if ( - !meta.redirect_uris || - !Array.isArray(meta.redirect_uris) || - meta.redirect_uris.length === 0 - ) { - res.status(400).json({ - error: "invalid_redirect_uri", - error_description: "At least one redirect_uri is required.", - }); - return; - } - const client = await registerClient(meta); - res.status(201).json(client as DcrResponse); - } catch (err: any) { - if (err instanceof DcrValidationError) { - res.status(400).json({ - error: "invalid_client_metadata", - error_description: err.message, - }); - return; - } - logger.error({ error: err.message }, "DCR error"); - res.status(500).json({ - error: "server_error", - error_description: "Failed to register client.", - }); - } - }, -); diff --git a/apps/api/src/oauth/server.ts b/apps/api/src/oauth/server.ts deleted file mode 100644 index db962221..00000000 --- a/apps/api/src/oauth/server.ts +++ /dev/null @@ -1,18 +0,0 @@ -import OAuth2Server from "@node-oauth/oauth2-server"; -import { oauthModel } from "./model"; - -export const oauth = new OAuth2Server({ - model: oauthModel, - allowEmptyState: true, - allowExtendedTokenAttributes: true, - accessTokenLifetime: Number(process.env.TOKEN_TTL_SECONDS) || 900, - refreshTokenLifetime: 60 * 60 * 24 * 30, // 30 days - authorizationCodeLifetime: 5 * 60, // 5 min - requireClientAuthentication: { - authorization_code: false, - refresh_token: false, - }, - alwaysIssueNewRefreshToken: true, -}); - -export { OAuth2Server }; diff --git a/apps/api/src/plan/model.ts b/apps/api/src/plan/model.ts deleted file mode 100644 index 69f7c61a..00000000 --- a/apps/api/src/plan/model.ts +++ /dev/null @@ -1,19 +0,0 @@ -import mongoose from "mongoose"; - -export interface Plan { - id: string; - maxFileSize: number; - maxStorage: number; -} - -const PlanSchema = new mongoose.Schema( - { - maxFileSize: { type: Number, required: true }, - maxStorage: { type: Number, required: true }, - }, - { - timestamps: true, - }, -); - -export default mongoose.models.Plan || mongoose.model("Plan", PlanSchema); diff --git a/apps/api/src/plan/queries.ts b/apps/api/src/plan/queries.ts deleted file mode 100644 index d08a4d7e..00000000 --- a/apps/api/src/plan/queries.ts +++ /dev/null @@ -1,5 +0,0 @@ -import PlanModel, { Plan } from "./model"; - -export async function getPlan(planId: string): Promise { - return await PlanModel.findById(planId); -} diff --git a/apps/api/src/services/log.ts b/apps/api/src/services/log.ts index 62056ae5..4e79ad81 100644 --- a/apps/api/src/services/log.ts +++ b/apps/api/src/services/log.ts @@ -1,5 +1,32 @@ -import pino from "pino"; +import { createObservability } from "@codelitdev/observability"; -const logger = pino(); +const posthogKey = process.env.POSTHOG_API_KEY || process.env.POSTHOG_ID; -export default logger; +const observability = createObservability({ + serviceName: "medialit-api", + environment: process.env.NODE_ENV || "development", + ...(posthogKey + ? { + posthog: { + apiKey: posthogKey, + host: process.env.POSTHOG_HOST, + }, + } + : {}), + logs: { level: process.env.LOG_LEVEL || "info" }, + contextPolicy: { + propertyAllowlist: new Set([ + "mediaId", + "status", + "path", + "method", + "code", + ]), + }, +}); + +export const captureException = + observability.captureException.bind(observability); +export const shutdownObservability = (timeoutMs: number) => + observability.shutdown(timeoutMs); +export default observability.logger; diff --git a/apps/api/src/services/mail.ts b/apps/api/src/services/mail.ts new file mode 100644 index 00000000..53e10393 --- /dev/null +++ b/apps/api/src/services/mail.ts @@ -0,0 +1,38 @@ +import nodemailer from "nodemailer"; +import logger from "./log"; + +export async function sendSignInCode( + email: string, + otp: string, +): Promise { + if (process.env.NODE_ENV !== "production") { + logger.info({ otp }, "Sign-in code generated"); + } + const host = process.env.EMAIL_HOST; + if (!host) { + if (process.env.NODE_ENV === "production") { + logger.error("Email is not configured; sign-in code was not sent"); + } + return; + } + // User and password are optional so Mailpit can receive mail without SMTP auth. + const user = process.env.EMAIL_USER; + const transporter = nodemailer.createTransport({ + host, + port: Number(process.env.EMAIL_PORT) || 587, + ...(user + ? { + auth: { + user, + pass: process.env.EMAIL_PASS, + }, + } + : {}), + }); + await transporter.sendMail({ + from: process.env.EMAIL_FROM || user || "medialit@localhost", + to: email, + subject: "Your MediaLit sign-in code", + text: `Enter this code to sign in: ${otp}`, + }); +} diff --git a/apps/api/src/signature/model.ts b/apps/api/src/signature/model.ts deleted file mode 100644 index 0f8f865c..00000000 --- a/apps/api/src/signature/model.ts +++ /dev/null @@ -1,42 +0,0 @@ -import mongoose from "mongoose"; -import { - SIGNATURE_LENGTH, - SIGNATURE_VALIDITY_MINUTES, -} from "../config/constants"; -import { getUniqueId } from "@medialit/utils"; - -export interface PreSignedUrl { - id: mongoose.Types.ObjectId; - userId: mongoose.Types.ObjectId; - apikey: string; - signature: string; - validTill: Date; - group?: string; -} - -const PreSignedUrlSchema = new mongoose.Schema( - { - userId: { type: mongoose.Schema.Types.ObjectId, required: true }, - apikey: { type: String, required: true }, - signature: { - type: String, - required: true, - default: () => getUniqueId(SIGNATURE_LENGTH), - }, - validTill: { - type: Date, - required: true, - default: () => - new Date( - new Date().getTime() + SIGNATURE_VALIDITY_MINUTES * 60000, - ), - }, - group: String, - }, - { - timestamps: true, - }, -); - -export default mongoose.models.PreSignedUrl || - mongoose.model("PreSignedUrl", PreSignedUrlSchema); diff --git a/apps/api/src/signature/queries.ts b/apps/api/src/signature/queries.ts index 8fb18a26..f3f0bedb 100644 --- a/apps/api/src/signature/queries.ts +++ b/apps/api/src/signature/queries.ts @@ -1,16 +1,28 @@ -import mongoose from "mongoose"; -import PreSignedUrlModel, { PreSignedUrl } from "./model"; +import { + createSignature, + deleteExpiredSignatures, + deleteSignatureById, + deleteSignatureValue, + getSignature, +} from "@/db"; + +export interface PreSignedUrl { + id: string; + userId: string; + apikey: string; + signature: string; + validTill: Date; + group?: string; +} export async function getPresignedUrl( signature: string, ): Promise { - return await PreSignedUrlModel.findOne({ signature }); + return getSignature(signature); } -export async function deletePresignedUrl( - id: mongoose.Types.ObjectId, -): Promise { - await PreSignedUrlModel.deleteOne({ id }); +export async function deletePresignedUrl(id: string): Promise { + await deleteSignatureById(id); } export async function createPresignedUrl( @@ -18,21 +30,13 @@ export async function createPresignedUrl( apikey: string, group?: string, ): Promise { - const presignedUrl = await PreSignedUrlModel.create({ - userId, - apikey, - group, - }); - return presignedUrl; + return createSignature({ userId, apikey, group }); } export async function cleanupExpiredLinks(userId: string): Promise { - await PreSignedUrlModel.deleteMany({ - userId, - validTill: { $lt: new Date().getTime() }, - }); + await deleteExpiredSignatures(userId); } export async function deleteBySignature(signature: string): Promise { - await PreSignedUrlModel.deleteOne({ signature }); + await deleteSignatureValue(signature); } diff --git a/apps/api/src/signature/routes.ts b/apps/api/src/signature/routes.ts index 561d433e..774f6c52 100644 --- a/apps/api/src/signature/routes.ts +++ b/apps/api/src/signature/routes.ts @@ -2,6 +2,8 @@ import express from "express"; import apikey from "../apikey/middleware"; import { getSignature } from "./handlers"; import { authenticatedApiLimiter } from "../auth/limiters"; +import { DATA_WRITE_SCOPE } from "../auth/options"; +import { requireScope } from "../auth/scopes"; const router = express.Router(); router.post( @@ -27,6 +29,7 @@ router.post( */ authenticatedApiLimiter, apikey, + requireScope(DATA_WRITE_SCOPE), getSignature, ); diff --git a/apps/api/src/signature/service.ts b/apps/api/src/signature/service.ts index 38d1c880..f7f7c070 100644 --- a/apps/api/src/signature/service.ts +++ b/apps/api/src/signature/service.ts @@ -1,7 +1,7 @@ import logger from "../services/log"; import { getUser } from "../user/queries"; import * as queries from "./queries"; -import { PreSignedUrl } from "./model"; +import { PreSignedUrl } from "./queries"; import { User } from "@medialit/models"; interface PresignedUrlProps { @@ -25,7 +25,7 @@ export async function getUserAndGroupFromPresignedUrl( return null; } - const user: User | null = await getUser(signedUrl!.userId.toString()); + const user: User | null = await getUser(String(signedUrl.userId)); if (!user) { return null; diff --git a/apps/api/src/swagger-generator.ts b/apps/api/src/swagger-generator.ts index 7c55f009..3c4e371f 100644 --- a/apps/api/src/swagger-generator.ts +++ b/apps/api/src/swagger-generator.ts @@ -81,7 +81,7 @@ const doc = { }; const outputFile = path.join(__dirname, "swagger_output.json"); -const routes = [path.join(__dirname, "index.ts")]; +const routes = [path.join(__dirname, "app.ts")]; swaggerAutogen()(outputFile, routes, doc).then(() => { // Post-process to inject Joi schemas directly (avoiding autogen inference) @@ -146,6 +146,23 @@ swaggerAutogen()(outputFile, routes, doc).then(() => { delete content.paths["/oauth/revoke"]; delete content.paths["/oauth/register"]; delete content.paths["/oauth/userinfo"]; + // The Dodo webhook router is mounted at /payment/webhook/dodo, but + // swagger-autogen records its route without the mount prefix. + delete content.paths["/"]; + for (const apiPath of Object.keys(content.paths)) { + if ( + apiPath.startsWith("/api/") || + apiPath.startsWith("/oauth/") || + apiPath.startsWith("/.well-known") || + apiPath.startsWith("/cleanup/") || + apiPath.startsWith("/payment/") || + apiPath === "/mcp" || + apiPath === "/ready" || + apiPath === "/openapi.json" + ) { + delete content.paths[apiPath]; + } + } } Object.entries(content.paths || {}).forEach(([apiPath, pathItem]: any) => { @@ -201,6 +218,26 @@ swaggerAutogen()(outputFile, routes, doc).then(() => { } operation.responses = operation.responses || {}; + // Authenticated routes reject requests that send more than one + // credential mechanism (ADR 0001 in the Platform repository). + if (operation.responses["401"] && !operation.responses["400"]) { + operation.responses["400"] = { + description: + "More than one credential was sent, for example both a Bearer token and an API key", + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/ErrorResponse", + }, + example: { + error: "credential_ambiguous", + error_description: + "Exactly one credential mechanism is allowed.", + }, + }, + }, + }; + } ["400", "401", "404", "409", "500"].forEach((statusCode) => { const existingResponse = operation.responses[statusCode]; if (!existingResponse || existingResponse.$ref) { diff --git a/apps/api/src/swagger_output.json b/apps/api/src/swagger_output.json index a5d113ae..e641056f 100644 --- a/apps/api/src/swagger_output.json +++ b/apps/api/src/swagger_output.json @@ -176,6 +176,20 @@ } } }, + "400": { + "description": "More than one credential was sent, for example both a Bearer token and an API key", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "error": "credential_ambiguous", + "error_description": "Exactly one credential mechanism is allowed." + } + } + } + }, "401": { "description": "Unauthorized", "content": { @@ -429,6 +443,20 @@ } } }, + "400": { + "description": "More than one credential was sent, for example both a Bearer token and an API key", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "error": "credential_ambiguous", + "error_description": "Exactly one credential mechanism is allowed." + } + } + } + }, "401": { "description": "Unauthorized", "content": { @@ -485,6 +513,20 @@ } } }, + "400": { + "description": "More than one credential was sent, for example both a Bearer token and an API key", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "error": "credential_ambiguous", + "error_description": "Exactly one credential mechanism is allowed." + } + } + } + }, "401": { "description": "Unauthorized", "content": { @@ -552,6 +594,20 @@ } } }, + "400": { + "description": "More than one credential was sent, for example both a Bearer token and an API key", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "error": "credential_ambiguous", + "error_description": "Exactly one credential mechanism is allowed." + } + } + } + }, "401": { "description": "Unauthorized", "content": { @@ -714,6 +770,20 @@ } } }, + "400": { + "description": "More than one credential was sent, for example both a Bearer token and an API key", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "error": "credential_ambiguous", + "error_description": "Exactly one credential mechanism is allowed." + } + } + } + }, "401": { "description": "Unauthorized", "content": { @@ -800,6 +870,20 @@ } } }, + "400": { + "description": "More than one credential was sent, for example both a Bearer token and an API key", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "example": { + "error": "credential_ambiguous", + "error_description": "Exactly one credential mechanism is allowed." + } + } + } + }, "401": { "description": "Unauthorized", "content": { diff --git a/apps/api/src/tus/cleanup.ts b/apps/api/src/tus/cleanup.ts index 34400e7c..5be13052 100644 --- a/apps/api/src/tus/cleanup.ts +++ b/apps/api/src/tus/cleanup.ts @@ -1,14 +1,12 @@ import logger from "../services/log"; -import TusUploadModel, { TusUpload } from "./model"; +import { deleteTusById, listExpiredTus } from "@/db"; import { removeTusFiles } from "./utils"; export async function cleanupTUSUploads() { logger.info({}, "Starting the tus uploads cleanup job"); const now = new Date(); - const expiredUploads = (await TusUploadModel.find({ - expiresAt: { $lt: now }, - }).lean()) as unknown as TusUpload[]; + const expiredUploads = await listExpiredTus(now); if (expiredUploads.length === 0) { logger.info("No expired tus uploads found to cleanup"); @@ -21,8 +19,10 @@ export async function cleanupTUSUploads() { ); for (const expiredUpload of expiredUploads) { - removeTusFiles(expiredUpload.tempFilePath); - await TusUploadModel.deleteOne({ _id: (expiredUpload as any)._id }); + if (expiredUpload.tempFilePath) { + removeTusFiles(expiredUpload.tempFilePath); + } + await deleteTusById(expiredUpload.id); } logger.info( diff --git a/apps/api/src/tus/finalize.ts b/apps/api/src/tus/finalize.ts index 8d2d7f32..79656120 100644 --- a/apps/api/src/tus/finalize.ts +++ b/apps/api/src/tus/finalize.ts @@ -6,7 +6,7 @@ import { } from "fs"; import path from "path"; import thumbnail from "@medialit/thumbnail"; -import mongoose from "mongoose"; + import { tempFileDirForUploads, imagePattern, @@ -51,7 +51,15 @@ export default async function finalizeUpload( tusUpload; const user = await getUser(userId); - if (!(await hasEnoughStorage(uploadLength, user!))) { + if (!user?.id) { + throw new Error(`User not found: ${userId}`); + } + if ( + !(await hasEnoughStorage(uploadLength, { + ...user, + _id: user._id || user.id, + })) + ) { throw new Error(NOT_ENOUGH_STORAGE); } @@ -131,7 +139,7 @@ export default async function finalizeUpload( const mediaObject = { fileName: `main.${fileExtension}`, mediaId: fileName.name, - userId: new mongoose.Types.ObjectId(userId), + userId, apikey, originalFileName: metadata.fileName, mimeType, diff --git a/apps/api/src/tus/model.ts b/apps/api/src/tus/model.ts deleted file mode 100644 index 44a1d1ef..00000000 --- a/apps/api/src/tus/model.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type { Media } from "@medialit/models"; -import mongoose from "mongoose"; - -export interface TusUpload { - uploadId: string; - userId: string; - apikey: string; - uploadLength: number; - uploadOffset: number; - metadata: Pick< - Media, - "fileName" | "mimeType" | "accessControl" | "caption" | "group" - >; - tempFilePath: string; - isComplete: boolean; - expiresAt?: Date; - signature?: string; -} - -const TusUploadSchema = new mongoose.Schema( - { - uploadId: { type: String, required: true, unique: true }, - userId: { type: String, required: true }, - apikey: { type: String, required: true }, - uploadLength: { type: Number, required: true }, - uploadOffset: { type: Number, required: true, default: 0 }, - metadata: { - fileName: { type: String, required: true }, - mimeType: { type: String, required: true }, - accessControl: { type: String, required: true, default: "private" }, - caption: String, - group: String, - }, - signature: String, - tempFilePath: String, - isComplete: Boolean, - expiresAt: Date, - }, - { timestamps: true }, -); - -export default mongoose.models.TusUpload || - mongoose.model("TusUpload", TusUploadSchema); diff --git a/apps/api/src/tus/queries.ts b/apps/api/src/tus/queries.ts index d6b587cf..8c98cc6f 100644 --- a/apps/api/src/tus/queries.ts +++ b/apps/api/src/tus/queries.ts @@ -1,55 +1,131 @@ import { SIGNATURE_VALIDITY_MINUTES } from "../config/constants"; -import TusUploadModel, { TusUpload } from "./model"; +import type { Media } from "@medialit/models"; +import { + createTusUploadRecord, + deleteTusUploadRecord, + getTusUploadRecord, + listTusByUser, + markTusComplete, + updateTusOffset, +} from "@/db"; -type TusUploadDocument = any; +export interface TusUpload { + uploadId: string; + userId: string; + apikey: string; + uploadLength: number; + uploadOffset: number; + metadata: Pick< + Media, + "fileName" | "mimeType" | "accessControl" | "caption" | "group" + >; + tempFilePath: string; + isComplete: boolean; + expiresAt?: Date; + signature?: string; +} + +function toTusMetadata(metadata: { + fileName: string; + mimeType: string; + accessControl: string; + caption?: string; + group?: string; +}): TusUpload["metadata"] { + return { + fileName: metadata.fileName, + mimeType: metadata.mimeType, + accessControl: + metadata.accessControl === "public" ? "public" : "private", + caption: metadata.caption, + group: metadata.group, + }; +} export async function createTusUpload( data: Omit, -): Promise { +): Promise { const expiresAt = new Date(); const signatureValidityHours = SIGNATURE_VALIDITY_MINUTES / 60; expiresAt.setHours(expiresAt.getHours() + signatureValidityHours); - - const tusUploadData: TusUpload = { + const created = await createTusUploadRecord({ uploadId: data.uploadId, userId: data.userId, apikey: data.apikey, uploadLength: data.uploadLength, - metadata: data.metadata, + metadata: { + fileName: data.metadata.fileName, + mimeType: data.metadata.mimeType, + accessControl: data.metadata.accessControl, + caption: data.metadata.caption, + group: data.metadata.group, + }, tempFilePath: data.tempFilePath, signature: data.signature, - uploadOffset: 0, - isComplete: false, expiresAt, + }); + return { + uploadId: created.uploadId, + userId: created.userId, + apikey: created.apikey, + uploadLength: created.uploadLength, + uploadOffset: created.uploadOffset, + metadata: toTusMetadata(created.metadata), + tempFilePath: created.tempFilePath || "", + isComplete: created.isComplete, + expiresAt: created.expiresAt, + signature: created.signature, }; - const tusUpload = await TusUploadModel.create(tusUploadData); - - return tusUpload; } export async function getTusUpload( uploadId: string, -): Promise { - return TusUploadModel.findOne({ uploadId }); +): Promise { + const row = await getTusUploadRecord(uploadId); + if (!row) return null; + return { + uploadId: row.uploadId, + userId: row.userId, + apikey: row.apikey, + uploadLength: row.uploadLength, + uploadOffset: row.uploadOffset, + metadata: toTusMetadata(row.metadata), + tempFilePath: row.tempFilePath || "", + isComplete: row.isComplete, + expiresAt: row.expiresAt, + signature: row.signature, + }; } export async function updateTusUploadOffset( uploadId: string, uploadOffset: number, ): Promise { - await TusUploadModel.updateOne({ uploadId }, { uploadOffset }); + await updateTusOffset(uploadId, uploadOffset); } export async function markTusUploadComplete(uploadId: string): Promise { - await TusUploadModel.updateOne({ uploadId }, { isComplete: true }); + await markTusComplete(uploadId); } export async function deleteTusUpload(uploadId: string): Promise { - await TusUploadModel.deleteOne({ uploadId }); + await deleteTusUploadRecord(uploadId); } export async function getTusUploadsByUserId( userId: string, -): Promise { - return TusUploadModel.find({ userId }).sort({ createdAt: -1 }); +): Promise { + const rows = await listTusByUser(userId); + return rows.map((row) => ({ + uploadId: row.uploadId, + userId: row.userId, + apikey: row.apikey, + uploadLength: row.uploadLength, + uploadOffset: row.uploadOffset, + metadata: toTusMetadata(row.metadata), + tempFilePath: row.tempFilePath || "", + isComplete: row.isComplete, + expiresAt: row.expiresAt, + signature: row.signature, + })); } diff --git a/apps/api/src/tus/tus-server.ts b/apps/api/src/tus/tus-server.ts index c2402d1b..645d3375 100644 --- a/apps/api/src/tus/tus-server.ts +++ b/apps/api/src/tus/tus-server.ts @@ -64,6 +64,17 @@ export const server = new Server({ }, }); +/** + * The group bound to the upload signature always wins over the one the + * client sends, so apps can trust it to say who an upload belongs to. + */ +export function uploadGroup( + signatureGroup: string | null | undefined, + clientGroup: string | undefined, +): string | undefined { + return signatureGroup ?? clientGroup; +} + export async function handleTusUploadCreate(req: any, upload: any) { const metadata = upload.metadata; const { user, apikey, group } = req; @@ -90,7 +101,7 @@ export async function handleTusUploadCreate(req: any, upload: any) { mimeType: metadata.mimeType || "application/octet-stream", accessControl: metadata.access, caption: metadata.caption, - group: metadata.group || group, + group: uploadGroup(group, metadata.group), }, tempFilePath: upload.id, }); diff --git a/apps/api/src/user/__test__/queries.test.js b/apps/api/src/user/__test__/queries.test.js deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/api/src/user/model.ts b/apps/api/src/user/model.ts deleted file mode 100644 index 326ace1c..00000000 --- a/apps/api/src/user/model.ts +++ /dev/null @@ -1,4 +0,0 @@ -import mongoose from "mongoose"; -import { UserSchema } from "@medialit/models"; - -export default mongoose.models.User || mongoose.model("User", UserSchema); diff --git a/apps/api/src/user/passport-strategies/jwt.ts.notused b/apps/api/src/user/passport-strategies/jwt.ts.notused deleted file mode 100644 index 09bbb224..00000000 --- a/apps/api/src/user/passport-strategies/jwt.ts.notused +++ /dev/null @@ -1,34 +0,0 @@ -import { Strategy, ExtractJwt, StrategyOptions } from "passport-jwt"; -import { jwtExpire, jwtSecret } from "../../config/constants"; -import UserModel from "../model"; -import { User } from "@medialit/models"; - -const jwtStrategyOptions: StrategyOptions = { - jwtFromRequest: ExtractJwt.fromBodyField("token"), - secretOrKey: jwtSecret, - jsonWebTokenOptions: { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore: VerifyOptions does not have the expiresIn option - expiresIn: jwtExpire, - }, - passReqToCallback: true, -}; - -export default new Strategy(jwtStrategyOptions, async function ( - _: any, - jwtToken: { email: string }, - done: any, -) { - const { email } = jwtToken; - const query = { email, active: true }; - try { - const user = await UserModel.findOne(query); - if (user) { - return done(null, user); - } else { - return done(null, false); - } - } catch (err: any) { - return done(err, false); - } -}); diff --git a/apps/api/src/user/passport-strategies/magic-link.ts.notused b/apps/api/src/user/passport-strategies/magic-link.ts.notused deleted file mode 100644 index b7072d8e..00000000 --- a/apps/api/src/user/passport-strategies/magic-link.ts.notused +++ /dev/null @@ -1,41 +0,0 @@ -import { Strategy } from "passport-magic-link"; -import { appName, jwtSecret } from "../../config/constants"; -import pug from "pug"; -import { send } from "../../services/mail"; -import signin from "../templates/signin"; -import { createUser, findByEmail } from "../queries"; -import createMagicLink from "../utils/create-magic-link"; -import { User } from "@medialit/models"; - -export default new Strategy( - { - secret: jwtSecret, - userFields: ["email"], - tokenField: "token", - passReqToCallbacks: true, - }, - async (req: any, user: any, token: string) => { - const magiclink = createMagicLink({ - protocol: req.protocol, - host: req.get("Host"), - token, - }); - const emailBody = pug.render(signin, { magiclink }); - return process.env.NODE_ENV === "production" - ? await send({ - to: user.email, - subject: `Sign in to ${appName}`, - body: emailBody, - }) - : console.log(`Login link: ${magiclink}`); - }, - async (req: any, user: User) => { - let dbUser: User | null = await findByEmail(user.email); - - if (!dbUser) { - dbUser = await createUser(user.email); - } - - return dbUser.active ? dbUser : null; - }, -); diff --git a/apps/api/src/user/queries.ts b/apps/api/src/user/queries.ts index 9b611349..9e2dfb29 100644 --- a/apps/api/src/user/queries.ts +++ b/apps/api/src/user/queries.ts @@ -1,18 +1,12 @@ import { SubscriptionStatus, User } from "@medialit/models"; -import UserModel from "./model"; -import mongoose from "mongoose"; -import { createApiKey } from "../apikey/queries"; +import { createAccount, findUserByEmail, findUserById } from "@/db"; -export async function getUser( - id: string, -): Promise<(User & { _id: mongoose.Types.ObjectId }) | null> { - return UserModel.findById(id); +export async function getUser(id: string): Promise { + return findUserById(id); } export async function findByEmail(email: string): Promise { - return await UserModel.findOne({ - email: email, - }); + return findUserByEmail(email); } export async function createUser( @@ -20,19 +14,9 @@ export async function createUser( name?: string, subscriptionStatus?: SubscriptionStatus, ): Promise { - const user = await UserModel.create({ + return createAccount({ email, - active: true, name, subscriptionStatus, }); - - // Automatically create a default API key for the new user - await createApiKey( - String(user.id || (user as any)._id), - process.env.DEFAULT_APP_NAME || "My Store", - true, - ); - - return user; } diff --git a/apps/api/src/user/templates/signin.ts b/apps/api/src/user/templates/signin.ts deleted file mode 100644 index f385989a..00000000 --- a/apps/api/src/user/templates/signin.ts +++ /dev/null @@ -1,33 +0,0 @@ -export default ` -doctype html -html - head - style(type='text/css'). - .cta-container { - margin: 32px 0px; - text-align: center; - } - .cta { - border: 1px solid #07077b; - border-radius: 4px; - padding: 4px 8px; - text-decoration: none; - color: white; - background-color: #07077b; - font-weight: bold; - } - .cta:hover { - background-color: #060665; - } - body - p To sign in, click the following link. - div(class="cta-container") - a( - href=\`\${magiclink}\` - class="cta" - ) Sign in - p - strong IMPORTANT: - | Do not share this email with anyone as anyone can log in to your - | account using the link in this email. -`; diff --git a/apps/api/test-preload.cjs b/apps/api/test-preload.cjs new file mode 100644 index 00000000..69213fc6 --- /dev/null +++ b/apps/api/test-preload.cjs @@ -0,0 +1,6 @@ +const buffer = require("buffer"); + +// jsonwebtoken -> jwa still reads SlowBuffer, which Node 26 removed. +if (!buffer.SlowBuffer) { + buffer.SlowBuffer = buffer.Buffer; +} diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json index 22c7430c..068c66fd 100644 --- a/apps/api/tsconfig.json +++ b/apps/api/tsconfig.json @@ -1,19 +1,20 @@ { - "compilerOptions": { - "outDir": "dist", - "target": "es5", - "module": "nodenext", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "moduleResolution": "nodenext", - "baseUrl": ".", - "paths": { - "@/*": [ - "./src/*" - ] - } - } -} \ No newline at end of file + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "noEmit": true, + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"], + "zod/v3": ["./node_modules/zod/v3/index.d.cts"] + }, + "types": ["node"] + }, + "include": ["src", "__tests__", "billing.config.ts", "drizzle.config.ts"] +} diff --git a/apps/docs/README.md b/apps/docs/README.md index dd12f474..80338f7f 100644 --- a/apps/docs/README.md +++ b/apps/docs/README.md @@ -8,7 +8,7 @@ Run development server: ```bash npm run dev # or -pnpm dev +bun dev # or yarn dev ``` diff --git a/apps/docs/app/global.css b/apps/docs/app/global.css index f8100af4..74cc55f8 100644 --- a/apps/docs/app/global.css +++ b/apps/docs/app/global.css @@ -1,4 +1,65 @@ @import "tailwindcss"; -@import "fumadocs-ui/css/neutral.css"; +@import "fumadocs-ui/css/lib/default-colors.css"; @import "fumadocs-ui/css/preset.css"; @import "fumadocs-openapi/css/preset.css"; + +/* Fumadocs applies this class from its packaged runtime. Tailwind does not + scan that runtime by default, so ensure the mobile subnav's desktop hide + utility is present in the compiled stylesheet. */ +@source inline("md:hidden"); + +/* The design system's tokens are imported in layout.tsx: Turbopack drops a + package @import here without an error. */ + +/* Keep Fumadocs' layout and components while sourcing every semantic color + from the shared CodeLit design system. `data-product="medialit"` on + selects MediaLit's purple accent family. */ +@theme inline { + --color-fd-background: var(--background); + --color-fd-foreground: var(--foreground); + --color-fd-card: var(--card); + --color-fd-card-foreground: var(--card-foreground); + --color-fd-popover: var(--popover); + --color-fd-popover-foreground: var(--popover-foreground); + --color-fd-primary: var(--primary); + --color-fd-primary-foreground: var(--primary-foreground); + --color-fd-secondary: var(--secondary); + --color-fd-secondary-foreground: var(--secondary-foreground); + --color-fd-muted: var(--muted); + --color-fd-muted-foreground: var(--muted-foreground); + --color-fd-accent: var(--accent); + --color-fd-accent-foreground: var(--accent-foreground); + --color-fd-border: var(--border); + --color-fd-ring: var(--ring); + --color-fd-overlay: var(--scrim); + --color-fd-error: var(--destructive); + --color-fd-warning: var(--warning); + --color-fd-success: var(--success); +} + +/* Fumadocs also reads these through var() at runtime (link underlines, callout + colors) and redefines them under .dark, which `@theme inline` doesn't + override. Declare them as plain variables too, for both themes. */ +:root, +.dark { + --color-fd-background: var(--background); + --color-fd-foreground: var(--foreground); + --color-fd-card: var(--card); + --color-fd-card-foreground: var(--card-foreground); + --color-fd-popover: var(--popover); + --color-fd-popover-foreground: var(--popover-foreground); + --color-fd-primary: var(--primary); + --color-fd-primary-foreground: var(--primary-foreground); + --color-fd-secondary: var(--secondary); + --color-fd-secondary-foreground: var(--secondary-foreground); + --color-fd-muted: var(--muted); + --color-fd-muted-foreground: var(--muted-foreground); + --color-fd-accent: var(--accent); + --color-fd-accent-foreground: var(--accent-foreground); + --color-fd-border: var(--border); + --color-fd-ring: var(--ring); + --color-fd-overlay: var(--scrim); + --color-fd-error: var(--destructive); + --color-fd-warning: var(--warning); + --color-fd-success: var(--success); +} diff --git a/apps/docs/app/icon.svg b/apps/docs/app/icon.svg index 3b2cd700..abdaf738 100644 --- a/apps/docs/app/icon.svg +++ b/apps/docs/app/icon.svg @@ -1 +1 @@ -
\ No newline at end of file +MediaLit logo \ No newline at end of file diff --git a/apps/docs/app/layout.config.tsx b/apps/docs/app/layout.config.tsx index 3dbcf7a3..2b8cc1eb 100644 --- a/apps/docs/app/layout.config.tsx +++ b/apps/docs/app/layout.config.tsx @@ -20,7 +20,12 @@ export const baseOptions: BaseLayoutProps = { > */} - Logo + MediaLit logo MediaLit Docs ), diff --git a/apps/docs/app/layout.tsx b/apps/docs/app/layout.tsx index 721b657b..af422546 100644 --- a/apps/docs/app/layout.tsx +++ b/apps/docs/app/layout.tsx @@ -1,16 +1,29 @@ +import "@codelitdev/design-system/styles.css"; import "./global.css"; import { RootProvider } from "fumadocs-ui/provider/next"; -import { Inter } from "next/font/google"; +import { Hanken_Grotesk, Spline_Sans_Mono } from "next/font/google"; import type { ReactNode } from "react"; -const inter = Inter({ +const hankenGrotesk = Hanken_Grotesk({ subsets: ["latin"], + variable: "--font-sans", +}); +const splineSansMono = Spline_Sans_Mono({ + subsets: ["latin"], + variable: "--font-mono", }); export default function Layout({ children }: { children: ReactNode }) { return ( - - + // `data-product` goes on so tokens aliased at :root pick up + // MediaLit's accent (see the design system's README). + + + + +```bash +medialit login +``` + + + + +```bash +medialit login --endpoint https://medialit.example.com +``` + +Later commands use the server you last logged in to. You can be logged in to several servers and pick one with `--endpoint`. + + + + +On a machine without a browser, add `--no-browser` and open the printed URL in a browser that can reach the machine's `127.0.0.1`, for example through an SSH tunnel. + +## Upload files + +```bash +medialit upload photo.jpg --public +``` + +The CLI prints each file's URL. Uploads are private unless you pass `--public`. + +Unlike the API and the MCP server, the CLI [seals](/concepts#temporary-uploads-and-sealing) each upload right away, because running the command is the decision to keep the file. Pass `--temp` to leave uploads temporary, so MediaLit deletes them after 24 hours unless you run `medialit seal `. Agents should use `--temp` for scratch files. + +Large files are sent in chunks and retried if the connection drops. + +": { type: "string", description: "Stored with every file in this upload." }, + "--group ": { type: "string", description: "Puts the files in a group." }, + "--temp": { type: "flag", description: "Don't seal the files." }, + }} +/> + +## Manage files + +```bash +medialit ls # newest first, 20 at a time +medialit ls --public --page 2 +medialit ls --group invoices +medialit get # prints the URL, a fresh one for private files +medialit seal +medialit rm +medialit whoami # server, account, app and storage used +medialit logout +``` + +## Use it from a coding agent + +Log in once yourself with `medialit login`. A coding agent running on the same machine can then run `medialit` like any other command, so you can ask it things like: + +> Take a screenshot of the new settings page, upload it to MediaLit as a public file and add the link to the pull request description. + +Tell the agent to add `--json` when it needs to read the result, for example the `mediaId` to delete the file later, and `--temp` for files it may not keep. + +**CLI or MCP?** Use the [MCP server](/mcp-server) for assistants that can't run commands, such as Claude or ChatGPT in the browser. Use the CLI for agents that can, especially for larger files: MCP uploads are limited to about 1.5 MB, while the CLI uploads files of any size your plan allows, in resumable chunks. + +## Use it in scripts + +Every command takes `--json`. URLs and JSON go to standard output and messages to standard error, so you can pipe the result: + +```bash +url=$(medialit upload build/report.pdf --public) +medialit ls --json | jq '.[].mediaId' +``` + +In CI, where nobody can open a browser, set an API key instead of logging in: + +```bash +export MEDIALIT_API_KEY=your_api_key +export MEDIALIT_ENDPOINT=https://medialit.example.com # only if you self-host +medialit upload dist/app.zip +``` + +## Where your login is stored + +In `~/.config/medialit/credentials.json`, or `%APPDATA%\medialit` on Windows. Only your user can read it. Set `MEDIALIT_CONFIG_DIR` to use another folder. `medialit logout` revokes the login on the server and removes it from the file. diff --git a/apps/docs/content/docs/concepts.mdx b/apps/docs/content/docs/concepts.mdx new file mode 100644 index 00000000..f40abac9 --- /dev/null +++ b/apps/docs/content/docs/concepts.mdx @@ -0,0 +1,84 @@ +--- +title: How MediaLit works +description: Apps, temporary uploads, access, groups and upload signatures. +icon: BookOpen +--- + +## Apps and API keys + +An app is a separate space for files, like a bucket. Each app has its own files, image settings and API key. Create one app per project or environment, for example `blog-production` and `blog-staging`. + +A request made with an app's API key only sees that app's files. When an agent connects to the [MCP server](/mcp-server) or you log in with the [CLI](/cli), you choose an app in the browser, and everything it does happens in that app. If you have only one app, it is chosen for you. + +## Temporary uploads and sealing + +Every upload to the API starts out **temporary**. A temporary file: + +- has a working URL, so you can show a preview right away +- does not appear in listings, counts, storage totals or the dashboard +- is deleted automatically after 24 hours + +**Sealing** keeps a file. Call seal when the file is actually used, for example when the user saves the form the file was uploaded from, or when an agent decides its output is worth keeping. + +This handles a problem plain object storage leaves to you. When a user uploads an avatar and then closes the tab without saving, the file would otherwise stay in your bucket forever. With MediaLit, you only seal files your app actually uses, and anything else is deleted for you. + +### Who seals + +| You upload with | The upload is | To keep it | +| --- | --- | --- | +| [REST API](/api/uploadMedia), [Node.js SDK](/node-sdk), [React](/react), [`@medialit/uploader`](/other-frameworks) | Temporary | Seal it from your server | +| [MCP server](/mcp-server) | Temporary | The agent calls `seal_media` | +| [CLI](/cli) | Sealed right away | Nothing to do. Pass `--temp` to leave it temporary | + +The CLI seals for you because running `medialit upload` is already the decision to keep the file; there is no later step, like saving a form, to seal at. An agent using the CLI for scratch files should pass `--temp`, so they are cleaned up like MCP uploads. + + + Sealing a public file moves it to permanent public storage, so its `file` URL changes. Store the URL returned by the seal call, or store the `mediaId` and look up the URL when you need it. + + +## Public and private files + +Choose `access` when you upload. It defaults to `private`. + +- **Public** files get a permanent URL that anyone can open, served through a CDN when one is configured. Use them for images on public pages, avatars and downloads you want to share. +- **Private** files get a signed URL that expires. Look the file up again with `get` when you need a fresh URL. Use them for documents, paid content and anything per-user. + +Until a file is sealed, its URL is a signed URL even if it is public. + +## Groups + +A group is a label for organising files inside an app, such as `avatars` or a customer ID. You can filter listings by group. + +When your server creates an upload signature with a group, every file uploaded with that signature goes into the group. + +## Upload signatures + +Browsers must never see your API key. Instead, your server asks MediaLit for an upload signature and passes it to the browser, and the browser uploads directly to MediaLit with it. The file does not pass through your server. + +1. The browser asks your server for a signature. +2. Your server checks that the user may upload, then creates a signature with its API key. +3. The browser uploads the file to MediaLit with the signature and gets the media back. +4. The browser saves the form with the `mediaId`, and your server seals it. + +A signature expires after 24 hours, or once a file has been uploaded with it. Only give signatures to users you trust to upload, because uploads count against your storage. + +[`createSignatureHandler`](/node-sdk#createsignaturehandler) creates the server route for you, and [`@medialit/react`](/react) handles the browser side. + +## Resumable uploads + +Browser uploads with [`@medialit/react`](/react) or [`@medialit/uploader`](/other-frameworks) use the [tus](https://tus.io) protocol. If the connection drops, the upload retries, and uploading the same file again continues from where it stopped. + +The REST endpoint `POST /media/create` uploads a file in a single request instead. It is simpler for server-to-server uploads. + +## Image processing + +MediaLit processes files as they are uploaded: + +- **Thumbnails** are generated for images and videos and returned as `thumbnail`. +- **WebP conversion**, when turned on in the app's settings, converts uploaded images to WebP. The original image is not kept. + +Change these settings with the [Node.js SDK](/node-sdk#getsettings-and-updatesettings), the [REST API](/api/uploadMedia) or the [MCP server](/mcp-server). + +## Limits + +Your plan sets the largest file you can upload and your total storage. Uploads that would go over either limit are refused with a message you can show to the user. When you [self-host](/self-hosting), you set the limits yourself. diff --git a/apps/docs/content/docs/dashboard.mdx b/apps/docs/content/docs/dashboard.mdx new file mode 100644 index 00000000..28068df3 --- /dev/null +++ b/apps/docs/content/docs/dashboard.mdx @@ -0,0 +1,33 @@ +--- +title: Dashboard +description: Manage your apps and files from the browser. +icon: LayoutDashboard +--- + +The [MediaLit dashboard](https://app.medialit.cloud) is where you manage apps and look at your files. Sign in with your email address. MediaLit sends you a one-time code, so there is no password to remember. + +## Apps + +Create an app for each project or environment. In an app's settings you can: + +- copy the app's API key +- rename the app +- delete the app + +## Files + +Open an app to browse its files, whether your app, an agent or a script uploaded them. Click a file to preview it and copy its link. + +Only sealed files are listed. Files that are still [temporary](/concepts#temporary-uploads-and-sealing) appear once they are sealed. + +## Account + +Your account page shows your plan and lets you manage billing. + +## Work with files from your terminal + +The [CLI](/cli) signs in the same way and works on the same files: `medialit upload report.pdf --public` prints the file's link. + +## Work with files through an AI assistant + +To upload, organise or delete files by asking in plain language, connect Claude, ChatGPT or another assistant to the [MCP server](/mcp-server). It works on the same files you see in the dashboard. diff --git a/apps/docs/content/docs/express-js.mdx b/apps/docs/content/docs/express-js.mdx index 74cf7902..5d412213 100644 --- a/apps/docs/content/docs/express-js.mdx +++ b/apps/docs/content/docs/express-js.mdx @@ -1,209 +1,120 @@ --- title: Express -description: Integrate MediaLit with Express +description: Use MediaLit from an Express backend. --- -This setup uses the `medialit` server-side SDK with Express and `multer`. -You can find a runnable reference app in [examples/express](https://github.com/codelitdev/medialit/tree/main/examples/express). +There are two ways to get files into MediaLit from an Express app: -## 1. Install dependencies +- **Your server uploads.** The browser sends the file to your server, which passes it on to MediaLit. This is simplest for small files and for files your server creates. +- **The browser uploads.** Your server hands out a short-lived signature and the browser sends the file straight to MediaLit. Large files don't pass through your server, and dropped connections resume. -```bash -pnpm add express multer medialit cors -``` +A runnable app is in [examples/express](https://github.com/codelitdev/medialit/tree/main/examples/express). -## 2. Add environment variables +## Set up -```bash -MEDIALIT_API_KEY=your_api_key_here -# Optional for self-hosted MediaLit -MEDIALIT_ENDPOINT=http://localhost:3001 -PORT=4000 +```npm +npm install express multer medialit ``` -## 3. Create Express server - -Create `server.js` (or `server.ts`) and add: +```bash title=".env" +MEDIALIT_API_KEY=your_api_key +# Only if you self-host MediaLit +# MEDIALIT_ENDPOINT=https://medialit.example.com +``` -```js +```js title="server.js" import express from "express"; -import cors from "cors"; import multer from "multer"; -import { readFile, unlink } from "node:fs/promises"; +import { unlink } from "node:fs/promises"; import { MediaLit } from "medialit"; const app = express(); +app.use(express.json()); + +const medialit = new MediaLit(); // reads MEDIALIT_API_KEY const upload = multer({ dest: "uploads/" }); -const client = new MediaLit(); +``` -app.use(cors()); -app.use(express.json()); +## Upload from your server -// Upload via server -app.post("/api/medialit/upload", upload.single("file"), async (req, res) => { - try { +```js title="server.js" +app.post("/photos", upload.single("file"), async (req, res) => { if (!req.file) return res.status(400).json({ error: "file is required" }); - if (!process.env.MEDIALIT_API_KEY) { - return res.status(500).json({ error: "MEDIALIT_API_KEY is missing" }); - } - const bytes = await readFile(req.file.path); - const formData = new FormData(); - formData.append( - "file", - new Blob([bytes], { type: req.file.mimetype }), - req.file.originalname || req.file.filename, - ); - formData.append("access", req.body.access === "public" ? "public" : "private"); - if (req.body.caption) formData.append("caption", req.body.caption); - if (req.body.group) formData.append("group", req.body.group); - - const uploadResponse = await fetch(`${client.endpoint}/media/create`, { - method: "POST", - headers: { - "x-medialit-apikey": process.env.MEDIALIT_API_KEY, - }, - body: formData, - }); - const media = await uploadResponse.json(); - if (!uploadResponse.ok) throw new Error(media.error || "Upload failed"); - if (!media.mediaId) throw new Error("Upload succeeded but mediaId is missing"); - - // Seal uploaded media so it persists and appears in list/get APIs. - const sealedMedia = await client.seal(media.mediaId); - - // cleanup temp file - await unlink(req.file.path).catch(() => undefined); - - return res.json(sealedMedia); - } catch (error) { - return res - .status(500) - .json({ error: error instanceof Error ? error.message : "Unknown error" }); - } + try { + const media = await medialit.upload(req.file.path, { + fileName: req.file.originalname, + mimeType: req.file.mimetype, + access: "public", + }); + // Your server decided to keep the file, so seal it right away. + const sealed = await medialit.seal(media.mediaId); + // Save sealed.mediaId and sealed.file in your database. + res.json(sealed); + } catch (err) { + res.status(500).json({ error: err.message }); + } finally { + await unlink(req.file.path).catch(() => {}); + } }); +``` -// List media -app.get("/api/medialit", async (req, res) => { - try { - const page = Number.parseInt(String(req.query.page ?? "1")); - const limit = Number.parseInt(String(req.query.limit ?? "10")); - const access = req.query.access; - const group = req.query.group; - - const media = await client.list(page, limit, { - access: access === "public" || access === "private" ? access : undefined, - group: typeof group === "string" ? group : undefined, - }); - - return res.json(media); - } catch (error) { - return res - .status(500) - .json({ error: error instanceof Error ? error.message : "Unknown error" }); - } -}); +Try it: -// Get media by id -app.get("/api/medialit/:id", async (req, res) => { - try { - const media = await client.get(req.params.id); - return res.json(media); - } catch (error) { - return res - .status(500) - .json({ error: error instanceof Error ? error.message : "Unknown error" }); - } -}); +```bash +curl -F "file=@photo.jpg" http://localhost:4000/photos +``` -// Delete media -app.delete("/api/medialit/:id", async (req, res) => { - try { - await client.delete(req.params.id); - return res.json({ success: true }); - } catch (error) { - return res - .status(500) - .json({ error: error instanceof Error ? error.message : "Unknown error" }); - } -}); +## Let browsers upload -// Count media -app.get("/api/medialit/count", async (_req, res) => { - try { - const count = await client.getCount(); - return res.json({ count }); - } catch (error) { - return res - .status(500) - .json({ error: error instanceof Error ? error.message : "Unknown error" }); - } -}); +Add a route that gives signed-in users an upload signature: -// Optional: signature for browser direct upload -app.post("/api/medialit/signature", async (req, res) => { - try { - const signature = await client.getSignature({ - group: req.body.group, - }); - - return res.json({ - endpoint: client.endpoint, - signature, - }); - } catch (error) { - return res - .status(500) - .json({ error: error instanceof Error ? error.message : "Unknown error" }); - } -}); - -const port = Number(process.env.PORT ?? 4000); -app.listen(port, () => { - console.log(`Server running on http://localhost:${port}`); +```js title="server.js" +app.post("/api/medialit/signature", requireUser, async (req, res) => { + try { + const signature = await medialit.getSignature({ + group: req.user.id, // optional + }); + res.json({ signature, endpoint: medialit.endpoint }); + } catch (err) { + res.status(500).json({ error: err.message }); + } }); ``` -## 4. Start server +`requireUser` stands for your app's authentication middleware. Anyone who can call this route can upload to your MediaLit app. -```bash -node --env-file=.env server.js -``` +In the browser, use [`@medialit/react`](/react) or [`@medialit/uploader`](/other-frameworks) with `signatureEndpoint: "/api/medialit/signature"`. When the user saves, send the `mediaId` to your server and seal it: -## 5. Test upload endpoint - -```bash -curl -X POST http://localhost:4000/api/medialit/upload \ - -F "file=@/absolute/path/to/image.jpg" \ - -F "access=public" \ - -F "caption=My first upload" +```js title="server.js" +app.post("/profile/avatar", requireUser, async (req, res) => { + const media = await medialit.seal(req.body.mediaId); + // Save media.mediaId and media.file with the user's profile. + res.json(media); +}); ``` -This example seals uploaded media as part of the same `/api/medialit/upload` route: +Files that are never sealed are deleted after 24 hours. See [sealing](/concepts#temporary-uploads-and-sealing). -```js -const sealedMedia = await client.seal(media.mediaId); -return res.json(sealedMedia); -``` +## List, get and delete -If you remove that line from the route, uploaded files may not persist or appear in list/get results. - -## 6. Get media list +```js title="server.js" +app.get("/photos", async (req, res) => { + const page = Number(req.query.page ?? 1); + res.json(await medialit.list(page, 20, { access: "public" })); +}); -```bash -curl "http://localhost:4000/api/medialit?page=1&limit=10" -``` +app.get("/photos/:id", async (req, res) => { + // Private files get a fresh signed URL each time. + res.json(await medialit.get(req.params.id)); +}); -Optional filters: +app.delete("/photos/:id", async (req, res) => { + await medialit.delete(req.params.id); + res.status(204).end(); +}); -```bash -curl "http://localhost:4000/api/medialit?page=1&limit=10&access=public" -curl "http://localhost:4000/api/medialit?page=1&limit=10&group=my-group" +app.listen(4000); ``` -## 7. Notes - -- Keep `MEDIALIT_API_KEY` server-side only. -- `medialit` SDK is Node.js server-only and should not run in browser code. -- For large client-side uploads, use `/api/medialit/signature` + TUS upload from the frontend. -- Seal uploaded files (`client.seal(mediaId)`) to persist them and make them show up in list/get results. +Run the server with `node --env-file=.env server.js`. See the [Node.js SDK](/node-sdk) for every method. diff --git a/apps/docs/content/docs/faqs.mdx b/apps/docs/content/docs/faqs.mdx index a1c1b5dc..77f47ad3 100644 --- a/apps/docs/content/docs/faqs.mdx +++ b/apps/docs/content/docs/faqs.mdx @@ -1,20 +1,36 @@ --- -title: Frequently asked questions -description: You asked, we answered +title: Frequently asked questions +description: Short answers to common questions. --- -#### Is MediaLit just a wrapper on top of the AWS S3 API? +#### Is MediaLit just a wrapper around S3? -No! It offers much more, like ease of use, image optimization, thumbnail creation, etc. +It stores files in S3-compatible storage, and adds what you would otherwise build around it: browser uploads without exposing keys, resumable uploads, automatic cleanup of unused uploads, public and private access, a list of your files with their details, thumbnails, quotas, an MCP server for agents, a dashboard and a CLI. See [Why MediaLit](/what-is-medialit). -#### Can I build a MediaLit alternative in a weekend, since I am a developer? +#### Why did my upload disappear? -You can always try. That said, we've already done all the heavy lifting for you, so you can add file uploads to your app in under 5 minutes. If time is NOT money, knock yourself out. +Uploads through the API and MCP are temporary until you seal them, and unsealed uploads are deleted after 24 hours. Seal a file when your app decides to keep it. The CLI seals uploads for you unless you pass `--temp`. See [who seals](/concepts#who-seals). - -Read [why](/what-is-medialit) we created MediaLit in the first place. - +#### Why did my file's URL change? -#### Is the MediaLit OSS crippled in any way? +Sealing a public file moves it to permanent public storage, which gives it a new URL. Store the URL that `seal` returns. Private files get a new signed URL each time you fetch them, so store the `mediaId` instead. -No! Everything that powers [medialit.cloud](https://medialit.cloud) is contained in our GitHub [repo](https://github.com/codelitdev/medialit). \ No newline at end of file +#### Can I upload from the browser without exposing my API key? + +Yes. Your server creates a short-lived upload signature and the browser uploads with it. See [upload signatures](/concepts#upload-signatures) and the [React uploader](/react). + +#### Can I upload from the terminal, a CI job or a coding agent? + +Yes, with the [CLI](/cli). Run `medialit login` on your machine, and you and any coding agent running there can use it. In CI, set `MEDIALIT_API_KEY` instead of logging in. + +#### Which AI assistants work with MediaLit? + +Any client that supports remote MCP servers, including Claude, ChatGPT, Cursor and VS Code. See [MCP server](/mcp-server). + +#### Can I use my own S3 or R2 bucket? + +Yes, by [self-hosting](/self-hosting) MediaLit. The hosted service at medialit.cloud stores files in MediaLit's own storage. + +#### Is the open-source version limited? + +No. Everything that runs [medialit.cloud](https://medialit.cloud) is in the [GitHub repository](https://github.com/codelitdev/medialit). diff --git a/apps/docs/content/docs/index.mdx b/apps/docs/content/docs/index.mdx index 1f98f05c..4cb8708c 100644 --- a/apps/docs/content/docs/index.mdx +++ b/apps/docs/content/docs/index.mdx @@ -1,18 +1,39 @@ --- -title: MediaLit -description: Effortless file uploads, optimization, and delivery. +title: MediaLit +description: Storage for agents, apps and humans. --- -Add file uploads to your apps in under 5 minutes. +import { BookOpen, Bot, Code, Rocket, User } from "lucide-react"; -## Features -- A simpler API for uploading files -- Delivery through a CDN -- Public/private files -- Automatic image optimization (configurable) -- Automatic thumbnail generation for images and videos (configurable) -- Unlimited uploads/downloads +MediaLit stores files and gives you a URL for each one. Your app uploads through an API. AI agents connect over MCP or run the CLI. People use the dashboard or the same CLI. They all work on the same files. -## Getting started + + }> + Connect Claude, ChatGPT, Cursor or any MCP client, or let a coding agent run the CLI, to save and share files. + + }> + Add a drop-in uploader to a React app, or use the REST API from any backend. + + }> + Browse, preview and share files from the dashboard, or upload from your terminal with the CLI. + + -You'll be off to the races in no time. Have a look at our [quick start](/quick-start). \ No newline at end of file +## What you get + +- **Uploads straight from the browser.** Your server hands out a short-lived signature, and the browser sends the file to MediaLit. Your API key never leaves your server, and large files resume after a dropped connection. +- **No orphaned files.** Uploads through the API and MCP are temporary until you seal them. If a user abandons a form, MediaLit deletes the file for you. [How this works](/concepts#temporary-uploads-and-sealing). +- **Public and private files.** Public files get a permanent URL. Private files get a signed URL that expires. +- **Thumbnails and WebP.** MediaLit makes thumbnails for images and videos, and can convert images to WebP when they are uploaded. +- **Open source.** Use [medialit.cloud](https://medialit.cloud), or [run it yourself](/self-hosting) on AWS S3, Cloudflare R2, MinIO or any S3-compatible storage. + +## Start here + + + }> + Upload your first file in a few minutes. + + }> + Apps, sealing, access and signatures. + + diff --git a/apps/docs/content/docs/mcp-server.mdx b/apps/docs/content/docs/mcp-server.mdx index a4f67d52..126a8235 100644 --- a/apps/docs/content/docs/mcp-server.mdx +++ b/apps/docs/content/docs/mcp-server.mdx @@ -6,12 +6,18 @@ icon: Terminal ## Introduction -MediaLit includes a built-in [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server. Instead of forcing external AI coding assistants (like Claude Code, Cursor, or ChatGPT) to interact through raw REST endpoints, they can query the MCP server to directly discover and run high-level tools. +Agents create files: reports, images, exports and screenshots. The MediaLit MCP server gives any MCP client a place to keep them and a link to share. Files an agent keeps appear in your [dashboard](/dashboard) and are available to your apps through the API. -The server uses the **Streamable HTTP** transport. Unlike standard stdio-based MCP servers that require running a separate subprocess, the MediaLit MCP server is mounted directly inside the main Express application at the `/mcp` route. This allows it to: -- Run on the same port and share the same SSL certificates. -- Reuse the application's authentication middlewares. -- Be accessible to both remote cloud-based clients (like ChatGPT) and local workspace assistants (like Claude Code). +Once connected, you can ask things like: + +- "Upload this chart as a public file and give me the link." +- "List the files I uploaded this week." +- "Delete the drafts you made earlier." +- "How much storage am I using?" + +Uploads from agents are [temporary until sealed](#two-step-upload-flow-drafts--sealing), so an agent's scratch files are cleaned up unless it, or you, decides to keep them. The [CLI](/cli) is different: it seals uploads right away unless the agent passes `--temp`. + +The server is at `https://api.medialit.cloud/mcp` and uses the Streamable HTTP transport, so there is nothing to install or run locally. --- @@ -28,12 +34,22 @@ Connecting with OAuth is straightforward: Once approved, you are logged in and the client will automatically handle connection keys in the background. - - When connecting via OAuth 2.0, the MCP server automatically associates the session with your **Default API Key**. Consequently, any files uploaded or managed via the MCP tools will be routed and stored under your **Default App**. +MediaLit supports Client ID Metadata Documents (CIMD) for current MCP clients and keeps Dynamic Client Registration (DCR) enabled for compatible clients. Registration identifies the client; users still sign in and approve access before it receives a token. + + + If you have more than one app, MediaLit asks which one to connect after you sign in. The agent's uploads and changes all happen in that app. Connect again to choose a different one. The [CLI](/cli) uses the same sign-in. +**Claude Code:** + +```bash +claude mcp add --transport http medialit https://api.medialit.cloud/mcp +``` + +Then run `/mcp` in Claude Code to sign in. + ### 2. API Key Setup (Alternative) If you are using developer-focused tools like Cursor or Claude Code and prefer a quick configuration, you can use your MediaLit account API key instead. @@ -151,10 +167,10 @@ Uploads a new file directly using base64-encoded payload parameters. - `access` *("public" | "private", optional)*: Access controls (default: `private`). - `group` *(string, optional)*: Custom folder/group tag. - **Output:** `{ mediaId: string }` -- **Limits:** Maximum payload size is restricted to **10MB**. For larger files, generate a signature and use the TUS protocol via REST. +- **Limits:** Requests to the MCP server are limited to 2 MB, and base64 makes a file about a third larger, so this tool works for files up to about 1.5 MB. For larger files, an agent that can run commands can use the [CLI](/cli), which uploads in resumable chunks. Otherwise, use `create_upload_signature` and upload the file with [`@medialit/uploader`](/other-frameworks) or the REST API. #### `create_upload_signature` -Generates a time-limited HMAC signature to authorize direct browser-to-bucket uploads. +Creates a short-lived [upload signature](/concepts#upload-signatures) that lets a browser or script upload one file to MediaLit without an API key. - **Inputs:** - `group` *(string, optional)*: Optional group to associate with the uploaded file. - **Output:** `{ signature: string }` diff --git a/apps/docs/content/docs/meta.json b/apps/docs/content/docs/meta.json index 0e6cd906..08be480d 100644 --- a/apps/docs/content/docs/meta.json +++ b/apps/docs/content/docs/meta.json @@ -1,19 +1,29 @@ { - "title": "Meta", - "pages": [ - "---Introduction---", - "quick-start", - "what-is-medialit", - "---Getting started---", - "next-app-router", - "express-js", - "---API Endpoints---", - "api", - "---MCP Server---", - "mcp-server", - "---Resources---", - "self-hosting", - "migrate-to-dual-bucket-architecture", - "faqs" - ] + "title": "Meta", + "pages": [ + "---Introduction---", + "index", + "quick-start", + "what-is-medialit", + "concepts", + "---For agents---", + "mcp-server", + "cli", + "---For apps---", + "next-app-router", + "react", + "other-frameworks", + "express-js", + "node-sdk", + "---For humans---", + "dashboard", + "---API Endpoints---", + "api", + "---Self-hosting---", + "self-hosting", + "---Resources---", + "faqs", + "---Upgrades---", + "upgrade-from-v0-4-0-to-v0-5-0" + ] } diff --git a/apps/docs/content/docs/migrate-to-dual-bucket-architecture.mdx b/apps/docs/content/docs/migrate-to-dual-bucket-architecture.mdx deleted file mode 100644 index be5bbda3..00000000 --- a/apps/docs/content/docs/migrate-to-dual-bucket-architecture.mdx +++ /dev/null @@ -1,49 +0,0 @@ ---- -title: Migrate to dual bucket architecture -description: Learn how to migrate from single bucket to dual bucket architecture ---- - -In order to make MediaLit truly vendor agnostic, we decided to move from a single bucket architecture to a dual bucket architecture. - -This is based on our years of learning from running MediaLit in production and all the community feedback we have received over the years. - -## Why dual bucket architecture? - -This makes the architecture portable across cloud providers. We discovered that providers like Backblaze B2 does not support hosting both private and public files in the same bucket. - -## How to migrate to dual bucket architecture? - -1. Create new buckets as detailed in [Self hosting](/self-hosting) section. - -2. Copy data from the old bucket to the new bucket. To do this, you will have to checkout MediaLit's Git [repo](https://github.com/codelitdev/medialit), then do the following in the terminal - -```sh -cd -pnpm i -pnpm --filter @medialit/scripts migrate:dual_bucket_architecture --dry-run # To see the changes without actually performing those -pnpm --filter @medialit/scripts migrate:dual_bucket_architecture -``` -3. Upgrade the MediaLit DB by running the following migration, by logging into your MediaLit's database and copy pasting the content of the file. - -`.migrations/00005-migrate-to-dual-bucket-architecture` - -4. Update .env file for the MediaLit API container. - -```sh -CLOUD_BUCKET_NAME=your-private-bucket -CLOUD_PUBLIC_BUCKET_NAME=your-public-bucket -PATH_PREFIX=bucket-folder-to-place-the-files-in # This is optional -CDN_ENDPOINT=endpoint - -# Optional, if using Cloudfront -ACCESS_PRIVATE_BUCKET_VIA_CLOUDFRONT=true -CLOUDFRONT_KEY_PAIR_ID=key -CLOUDFRONT_PRIVATE_KEY=priv-key -``` - -5. Re-deploy the MediaLit API container. -``` -docker compose pull -docker compose down -docker compose up -d -``` \ No newline at end of file diff --git a/apps/docs/content/docs/next-app-router.mdx b/apps/docs/content/docs/next-app-router.mdx index 953d0f6a..832e3098 100644 --- a/apps/docs/content/docs/next-app-router.mdx +++ b/apps/docs/content/docs/next-app-router.mdx @@ -1,249 +1,156 @@ --- -title: Nextjs App Router -description: Integrate MediaLit with Nextjs App Router +title: Next.js +description: Add file uploads to a Next.js App Router app. --- -This guide mirrors our working demo in [examples/next-app-router](https://github.com/codelitdev/medialit/tree/main/examples/next-app-router) and uses the `medialit` package. +You will add an uploader that sends files from the browser straight to MediaLit, then keep the file when the user saves. The full app is in [examples/next-app-router](https://github.com/codelitdev/medialit/tree/main/examples/next-app-router). -## 1. Install dependencies + + -```bash -pnpm add medialit tus-js-client -``` - -## 2. Add environment variables +### Install the packages -```bash -MEDIALIT_API_KEY=your_api_key_here -# Optional for self-hosted MediaLit -MEDIALIT_ENDPOINT=http://localhost:3001 +```npm +npm install medialit @medialit/react ``` -## 3. Create API routes (server-side only) +`medialit` runs on your server and holds the API key. `@medialit/react` is the browser uploader. -Create `app/api/medialit/route.ts`: + + -```ts -import { NextRequest } from "next/server"; -import { MediaLit } from "medialit"; +### Add your API key -const client = new MediaLit(); - -export async function GET(request: NextRequest) { - const searchParams = request.nextUrl.searchParams; - const mediaId = searchParams.get("mediaId"); - const page = Number.parseInt(searchParams.get("page") ?? "1"); - const limit = Number.parseInt(searchParams.get("limit") ?? "10"); - const access = searchParams.get("access") as "public" | "private" | undefined; - const group = searchParams.get("group") ?? undefined; - - try { - if (mediaId) { - const media = await client.get(mediaId); - return Response.json(media); - } - - const media = await client.list(page, limit, { access, group }); - return Response.json(media); - } catch (error) { - return Response.json( - { error: error instanceof Error ? error.message : "Unknown error" }, - { status: 500 }, - ); - } -} +Add the key from your app's settings in the [dashboard](https://app.medialit.cloud) to `.env.local`: -export async function POST() { - try { - const signature = await client.getSignature(); - - return Response.json({ - endpoint: client.endpoint, - signature, - }); - } catch (error) { - return Response.json( - { error: error instanceof Error ? error.message : "Unknown error" }, - { status: 500 }, - ); - } -} - -export async function DELETE(request: NextRequest) { - const mediaId = request.nextUrl.searchParams.get("mediaId"); +```bash title=".env.local" +MEDIALIT_API_KEY=your_api_key +# Only if you self-host MediaLit +# MEDIALIT_ENDPOINT=https://medialit.example.com +``` - if (!mediaId) { - return Response.json({ error: "Media ID is required" }, { status: 400 }); - } + + - try { - await client.delete(mediaId); - return Response.json({ success: true }); - } catch (error) { - return Response.json( - { error: error instanceof Error ? error.message : "Unknown error" }, - { status: 500 }, - ); - } -} +### Create the signature route -export async function PATCH(request: NextRequest) { - const mediaId = request.nextUrl.searchParams.get("mediaId"); +The uploader asks this route for a short-lived upload signature, so the API key never reaches the browser. - if (!mediaId) { - return Response.json({ error: "Media ID is required" }, { status: 400 }); - } +```ts title="app/api/medialit/signature/route.ts" +import { createSignatureHandler } from "medialit"; +import { auth } from "@/auth"; // your app's authentication - try { - const media = await client.seal(mediaId); - return Response.json(media); - } catch (error) { - return Response.json( - { error: error instanceof Error ? error.message : "Unknown error" }, - { status: 500 }, - ); - } -} +export const POST = createSignatureHandler({ + authorize: async () => { + const session = await auth(); + return !!session; + }, +}); ``` -Create `app/api/medialit/[id]/route.ts`: + + Anyone who passes `authorize` can upload to your MediaLit app. Return `false` for visitors who are not signed in. + -```ts -import { MediaLit } from "medialit"; +To put each user's files in their own group, return a group instead of `true`: -const client = new MediaLit(); +```ts +authorize: async () => { + const session = await auth(); + return session ? { group: session.user.id } : false; +}, +``` -export async function GET( - _request: Request, - { params }: { params: Promise<{ id: string }> }, -) { - try { - const { id } = await params; - const media = await client.get(id); - return Response.json(media); - } catch (error) { - return Response.json( - { error: error instanceof Error ? error.message : "Unknown error" }, - { status: 500 }, + + + +### Add the uploader + +```tsx title="app/profile/avatar-upload.tsx" +"use client"; + +import { useState } from "react"; +import { MediaLitUploader } from "@medialit/react"; +import "@medialit/react/styles.css"; +import { saveAvatar } from "./actions"; + +export function AvatarUpload() { + const [mediaId, setMediaId] = useState(); + + return ( +
mediaId && saveAvatar(mediaId)}> + setMediaId(media.mediaId)} + /> + + ); - } } ``` -Create `app/api/medialit/count/route.ts`: +The uploader shows a drop zone, upload progress with a cancel button, and the uploaded file. Read [React](/react) to change its text, style or behaviour. -```ts -import { MediaLit } from "medialit"; +
+ -const client = new MediaLit(); +### Seal the file when the user saves -export async function GET() { - try { - const count = await client.getCount(); - return Response.json({ count }); - } catch (error) { - return Response.json( - { error: error instanceof Error ? error.message : "Unknown error" }, - { status: 500 }, - ); - } -} -``` +The upload is temporary until you seal it. If the user leaves without saving, MediaLit deletes it after 24 hours. -## 4. Client upload + seal (standard upload) +```ts title="app/profile/actions.ts" +"use server"; -In a client component, request signature from your route, then upload to MediaLit directly: +import { MediaLit } from "medialit"; -```ts -const presigned = await fetch("/api/medialit", { method: "POST" }); -const { endpoint, signature } = await presigned.json(); - -const formData = new FormData(); -formData.append("file", file); -formData.append("caption", caption); -formData.append("access", isPublic ? "public" : "private"); - -await fetch(`${endpoint}/media/create`, { - method: "POST", - headers: { - "x-medialit-signature": signature, - }, - body: formData, -}); +const medialit = new MediaLit(); + +export async function saveAvatar(mediaId: string) { + const media = await medialit.seal(mediaId); + // Save media.mediaId and media.file with the user's profile. +} ``` -After upload, resolve media details and seal the file: +Sealing a public file gives it its permanent URL, so save the `file` returned by `seal`, not the one from the upload. -```ts -const uploadResponse = await fetch(`${endpoint}/media/create`, { - method: "POST", - headers: { - "x-medialit-signature": signature, - }, - body: formData, -}); -const uploaded = await uploadResponse.json(); + +
-const mediaResponse = await fetch(`/api/medialit?mediaId=${uploaded.mediaId}`); -const media = await mediaResponse.json(); +## Show private files -await fetch(`/api/medialit?mediaId=${media.mediaId}`, { - method: "PATCH", -}); +Private files have signed URLs that expire. Store the `mediaId` and get a fresh URL when you render the page: -window.dispatchEvent(new Event("medialit:refresh")); -``` +```tsx title="app/documents/[id]/page.tsx" +import { MediaLit } from "medialit"; -## 5. TUS resumable upload (with seal) +const medialit = new MediaLit(); -```ts -import { Upload } from "tus-js-client"; - -const { endpoint, signature } = await (await fetch("/api/medialit", { - method: "POST", -})).json(); - -const upload = new Upload(file, { - endpoint: `${endpoint}/media/create/resumable`, - chunkSize: 1024000, - retryDelays: [0, 3000, 5000], - headers: { "x-medialit-signature": signature }, - metadata: { - fileName: file.name, - mimeType: file.type, - access: "private", - caption: "", - }, - onAfterResponse: (_req, res) => { - const mediaHeader = res.getHeader("media"); - if (mediaHeader) { - const media = JSON.parse(mediaHeader); - // Keep media.mediaId for sealing. - } - }, -}); +export default async function DocumentPage({ + params, +}: { + params: Promise<{ id: string }>; +}) { + const { id } = await params; + const document = await getDocument(id); // from your database + const media = await medialit.get(document.mediaId); -upload.start(); + return Download {media.originalFileName}; +} ``` -When upload completes, seal the returned `mediaId`: +## Delete files ```ts -await fetch(`/api/medialit?mediaId=${media.mediaId}`, { - method: "PATCH", -}); -window.dispatchEvent(new Event("medialit:refresh")); +await medialit.delete(mediaId); ``` -## 6. Run - -```bash -pnpm dev -``` - -## 7. Important note - -Both upload methods should seal files before considering upload complete in UI. -Unsealed files are temporary and may not appear in normal list/get results. +## Next steps -If you want the full working UI (standard upload + TUS upload + listing + seal flow), use `examples/next-app-router` as reference directly. +- [React](/react) covers every prop of `MediaLitUploader` and the `useMediaLitUpload` hook for your own UI. +- [Node.js SDK](/node-sdk) lists everything the server SDK can do. +- [How MediaLit works](/concepts) explains sealing, access and groups. diff --git a/apps/docs/content/docs/node-sdk.mdx b/apps/docs/content/docs/node-sdk.mdx new file mode 100644 index 00000000..c72263f4 --- /dev/null +++ b/apps/docs/content/docs/node-sdk.mdx @@ -0,0 +1,141 @@ +--- +title: Node.js SDK +description: Reference for the medialit server SDK. +--- + +The `medialit` package calls the MediaLit API with your app's API key. It runs on the server only and refuses to start in a browser. + +```npm +npm install medialit +``` + +Node.js 18 or later is required. + +## new MediaLit() + +```ts +import { MediaLit } from "medialit"; + +const medialit = new MediaLit({ + apiKey: process.env.MEDIALIT_API_KEY, + endpoint: process.env.MEDIALIT_ENDPOINT, +}); +``` + + + +Every method throws an `Error` with MediaLit's message when a request fails. + +## upload + +```ts +const media = await medialit.upload("./photo.jpg", { + access: "public", + caption: "Team photo", +}); +``` + +Uploads a file path, a `Buffer` or a readable stream in one request. The file is [temporary until you seal it](/concepts#temporary-uploads-and-sealing). + + + +## seal + +```ts +const media = await medialit.seal(mediaId); +``` + +Keeps a temporary upload. Returns the media with its final `file` URL, which changes for public files. Sealing a file that is already sealed returns it unchanged. + +## get + +```ts +const media = await medialit.get(mediaId); +``` + +Returns one file, including temporary ones. Private files come with a new signed URL each time. + +## list + +```ts +const media = await medialit.list(1, 20, { access: "public", group: "avatars" }); +``` + +Returns a page of sealed files, newest first. The arguments are the page number, the page size and optional `access` and `group` filters. + +## delete + +```ts +await medialit.delete(mediaId); +``` + +Deletes the file and its thumbnail. + +## getCount and getStats + +```ts +const count = await medialit.getCount(); // sealed files +const { storage, maxStorage } = await medialit.getStats(); // bytes +``` + +## getSettings and updateSettings + +```ts +await medialit.updateSettings({ useWebP: true, webpOutputQuality: 80 }); +const settings = await medialit.getSettings(); +``` + +Image processing settings for the app. `useWebP` converts uploaded images to WebP, at a `webpOutputQuality` from 0 to 100. + +## getSignature + +```ts +const signature = await medialit.getSignature({ group: "avatars" }); +``` + +Creates an [upload signature](/concepts#upload-signatures) for a browser. Pass it to the browser together with `medialit.endpoint`. Files uploaded with it go into `group` when you pass one. + +## createSignatureHandler + +Creates a `POST` route that returns `{ signature, endpoint }` for [`@medialit/react`](/react) and [`@medialit/uploader`](/other-frameworks). It works with any framework that uses the Fetch API's `Request` and `Response`, such as Next.js, Hono, Remix and Bun. For Express, see [Let browsers upload](/express-js#let-browsers-upload). + +```ts title="app/api/medialit/signature/route.ts" +import { createSignatureHandler } from "medialit"; + +export const POST = createSignatureHandler({ + authorize: async (request) => { + const user = await getUser(request); // your app's authentication + return user ? { group: user.id } : false; + }, +}); +``` + + boolean | { group?: string } | null | Promise<...>", + required: true, + description: "Return false or null to refuse with 401, true to allow, or { group } to allow and put the uploads in a group. Anyone who passes this check can upload to your MediaLit app.", + }, + apiKey: { type: "string", description: "Same as the constructor. Read when the first request arrives." }, + endpoint: { type: "string", description: "Same as the constructor." }, + client: { type: "MediaLit", description: "An existing client to use instead of apiKey and endpoint." }, + publicEndpoint: { type: "string", description: "The MediaLit URL for the browser when it differs from the one your server uses, for example inside Docker." }, + }} +/> + +If MediaLit refuses to create a signature, the route responds with 500 and `{ error }`. diff --git a/apps/docs/content/docs/other-frameworks.mdx b/apps/docs/content/docs/other-frameworks.mdx new file mode 100644 index 00000000..eaad0402 --- /dev/null +++ b/apps/docs/content/docs/other-frameworks.mdx @@ -0,0 +1,163 @@ +--- +title: Other frameworks +description: Upload from the browser with Vue, Svelte or plain JavaScript. +--- + +`@medialit/uploader` uploads a file from the browser to MediaLit and has no framework dependency. [`@medialit/react`](/react) is built on it. + +```npm +npm install @medialit/uploader +``` + +Your server needs a route that returns `{ signature, endpoint }`. Use [`createSignatureHandler`](/node-sdk#createsignaturehandler) with any server that uses the Fetch API, or see [Express](/express-js#let-browsers-upload). + +## Upload a file + +```ts +import { uploadFile } from "@medialit/uploader"; + +const media = await uploadFile(file, { + signatureEndpoint: "/api/medialit/signature", + access: "public", + onProgress: ({ percentage }) => console.log(`${Math.round(percentage)}%`), +}); + +console.log(media.mediaId, media.file); +``` + +`uploadFile` resolves to the [uploaded media](/react#uploadedmedia) and rejects with an error whose `message` you can show to the user. Errors from MediaLit or your signature route are `MediaLitUploadError`s with the HTTP `status`. Send `media.mediaId` to your server and seal it there when the user saves. + +## Cancel an upload + +Pass an `AbortSignal`. Cancelling rejects with an `UploadAbortedError`. + +```ts +import { isUploadAbortedError, uploadFile } from "@medialit/uploader"; + +const controller = new AbortController(); +cancelButton.onclick = () => controller.abort(); + +try { + await uploadFile(file, { + signatureEndpoint: "/api/medialit/signature", + signal: controller.signal, + }); +} catch (err) { + if (!isUploadAbortedError(err)) showError(err.message); +} +``` + +## Examples + + + + +With a bundler such as Vite: + +```html + + + + +``` + + + + +```vue + + + +``` + + + + +```svelte + + + + +{#if error}

{error}

{/if} +``` + +
+
+ +## Options + + Promise<{ signature: string; endpoint: string }>", + description: "Use instead of signatureEndpoint to fetch the signature yourself.", + }, + access: { type: '"public" | "private"', default: '"private"' }, + caption: { type: "string" }, + fileName: { type: "string", description: "Required for a Blob that is not a File." }, + chunkSize: { type: "number", description: "Bytes per request. By default the file is sent in one request." }, + retryDelays: { type: "number[]", default: "[0, 3000, 5000, 10000]", description: "Milliseconds to wait before each retry." }, + resume: { type: "boolean", default: "true", description: "Continue an interrupted upload of the same file." }, + signal: { type: "AbortSignal", description: "Cancels the upload." }, + onProgress: { type: "({ bytesUploaded, bytesTotal, percentage }) => void" }, + }} +/> diff --git a/apps/docs/content/docs/quick-start.mdx b/apps/docs/content/docs/quick-start.mdx index 11adfed6..03f66c65 100644 --- a/apps/docs/content/docs/quick-start.mdx +++ b/apps/docs/content/docs/quick-start.mdx @@ -1,43 +1,128 @@ --- -title: Quick Start -description: Getting started with MediaLit -icon: Album +title: Quick start +description: Upload your first file to MediaLit. +icon: Rocket --- -## Introduction +import { Bot, Code, Server, Terminal } from "lucide-react"; -[MediaLit](https://medialit.cloud) offers **file hosting** along with other niceties like **image optimization** and **thumbnail generation**. + + - +### Create an app - - Read our in-depth [What is MediaLit](/what-is-medialit) introduction. +Sign in to the [MediaLit dashboard](https://app.medialit.cloud) with your email and create an app. Each app has its own files, settings and API key. Copy the API key from the app's settings. + + + Anyone with the API key can upload, read and delete the app's files. Keep it on your server and out of browser code. -## Getting started + + -You have to create an app on [MediaLit dashboard](https://app.medialit.cloud) and obtain the API key to the app in order to start uploading. +### Upload a file -Once you have the API key, select your framework from below OR simply use our REST API to start uploading to MediaLit. +Pick the way you want to work with MediaLit. -## Pick your framework + + -### Frontend frameworks +```npm +npm install -g @medialit/cli +``` - - } /> - +```bash +medialit login +medialit upload photo.jpg --public +``` -### Backend frameworks +The CLI seals the file for you, so you can skip the next step. See [CLI](/cli). - - } /> - + + + +```bash +curl -X POST https://api.medialit.cloud/media/create \ + -H "x-medialit-apikey: YOUR_API_KEY" \ + -F "file=@photo.jpg" \ + -F "access=public" +``` + + + + +```npm +npm install medialit +``` + +```ts +import { MediaLit } from "medialit"; + +const medialit = new MediaLit({ apiKey: process.env.MEDIALIT_API_KEY }); +const media = await medialit.upload("./photo.jpg", { access: "public" }); +``` + + + + +Add `https://api.medialit.cloud/mcp` as a remote MCP server in Claude, ChatGPT, Cursor or another MCP client, sign in, and ask: + +> Upload this image to MediaLit as a public file and give me its link. + +See [MCP server](/mcp-server) for client set-up. + + + + +The response includes a `mediaId` and a `file` URL. + + + + +### Seal it + +The upload is temporary, and MediaLit deletes it after 24 hours unless you seal it. Seal files once you know you want to keep them, for example when the user saves the form they were uploaded from. + + + + +```bash +curl -X POST https://api.medialit.cloud/media/seal/MEDIA_ID \ + -H "x-medialit-apikey: YOUR_API_KEY" +``` + + + + +```ts +const sealed = await medialit.seal(media.mediaId); +console.log(sealed.file); +``` + + + + +Sealed files appear in listings, in your storage total and in the dashboard. [Why uploads are temporary](/concepts#temporary-uploads-and-sealing). -### Un-supported framework or language? + + -Our REST API is well documented and is easy to use. Write your own wrapper or go full CURL mode. +## Next steps - } /> + }> + Add a drop-in uploader to your app. + + }> + Vue, Svelte or plain JavaScript in the browser. + + }> + Upload from a Node.js backend. + + }> + Give AI agents a place to keep files. + + }> + Use MediaLit from any language. + diff --git a/apps/docs/content/docs/react.mdx b/apps/docs/content/docs/react.mdx new file mode 100644 index 00000000..a471382f --- /dev/null +++ b/apps/docs/content/docs/react.mdx @@ -0,0 +1,185 @@ +--- +title: React +description: The MediaLitUploader component and the useMediaLitUpload hook. +--- + +`@medialit/react` uploads files from the browser directly to MediaLit with resumable uploads. It works with any React 18 or 19 app, including Next.js, Remix and Vite. + +```npm +npm install @medialit/react +``` + +Your server needs a route that returns an upload signature. See [Next.js](/next-app-router#create-the-signature-route) or [`createSignatureHandler`](/node-sdk#createsignaturehandler). + +## MediaLitUploader + +A drop zone with upload progress, cancel, retry and a preview of the uploaded file. + +```tsx +"use client"; + +import { MediaLitUploader } from "@medialit/react"; +import "@medialit/react/styles.css"; + +export function Upload() { + return ( + console.log(media.mediaId)} + /> + ); +} +``` + +### Props + + Promise<{ signature: string; endpoint: string }>", + description: "Use instead of signatureEndpoint when you need to fetch the signature yourself, for example with an Authorization header.", + }, + access: { + type: '"public" | "private"', + default: '"private"', + description: "Who can open the file once it is sealed.", + }, + caption: { type: "string", description: "Stored with the file." }, + accept: { + type: "string", + description: 'File types to allow, in the format of , such as "image/*,.pdf". Also checked for dropped files.', + }, + maxFileSize: { + type: "number", + description: "Largest file in bytes. MediaLit also enforces your plan's limit.", + }, + chunkSize: { + type: "number", + description: "Bytes per request. By default the file is sent in one request.", + }, + onUploadComplete: { type: "(media: UploadedMedia) => void" }, + onUploadError: { type: "(error: MediaLitUploadError) => void" }, + labels: { + type: "Partial", + description: "Replace any text, for example to translate it.", + }, + disabled: { type: "boolean", default: "false" }, + className: { type: "string", description: "Added to the root element." }, + }} +/> + +### Translate the text + +```tsx + `Taille maximale : ${maxSize}`, + }} +/> +``` + +### Style it + +`@medialit/react/styles.css` is optional. It follows the system's light or dark mode, and you can change its colours and corners with CSS variables: + +```css +.medialit-uploader { + --medialit-accent: #0f766e; + --medialit-accent-contrast: #ffffff; + --medialit-radius: 6px; + --medialit-font: inherit; +} +``` + +The other variables are `--medialit-text`, `--medialit-muted`, `--medialit-border`, `--medialit-surface`, `--medialit-surface-hover`, `--medialit-track`, `--medialit-success` and `--medialit-error`. + +To style it from scratch, skip the stylesheet and target the `medialit-uploader__*` classes. The root element has `data-status` set to `idle`, `uploading`, `success` or `error`. + +## useMediaLitUpload + +The same upload logic without any markup, for when you want your own UI. + +```tsx +"use client"; + +import { useMediaLitUpload } from "@medialit/react"; + +export function UploadButton() { + const { upload, cancel, status, progress, media, error } = + useMediaLitUpload({ + signatureEndpoint: "/api/medialit/signature", + access: "public", + }); + + if (status === "uploading") { + return ( + + ); + } + + return ( +
+ { + const file = e.target.files?.[0]; + if (file) upload(file); + }} + /> + {media && } + {error &&

{error.message}

} +
+ ); +} +``` + +It takes the same options as `MediaLitUploader` except `accept`, `maxFileSize`, `labels`, `disabled` and `className`, and returns: + + Promise", + description: "Starts an upload and cancels any upload already running. Resolves to null if the upload fails or is cancelled. It never rejects. The options override the hook's options for this upload.", + }, + cancel: { type: "() => void", description: "Stops the upload and returns to idle." }, + reset: { type: "() => void", description: "Same as cancel. Use it to clear a finished upload." }, + status: { type: '"idle" | "uploading" | "success" | "error"' }, + progress: { type: "number", description: "0 to 100." }, + file: { type: "File | null", description: "The file being uploaded or last uploaded." }, + media: { type: "UploadedMedia | null", description: "Set when status is success." }, + error: { type: "MediaLitUploadError | null", description: "Set when status is error. message is MediaLit's reason, such as a file over your plan's limit." }, + }} +/> + +## UploadedMedia + +What an upload returns. + + + + + Uploads are temporary until your server seals them. See [sealing](/concepts#temporary-uploads-and-sealing). + diff --git a/apps/docs/content/docs/self-hosting.mdx b/apps/docs/content/docs/self-hosting.mdx index 71735fc3..436c4b51 100644 --- a/apps/docs/content/docs/self-hosting.mdx +++ b/apps/docs/content/docs/self-hosting.mdx @@ -3,7 +3,9 @@ title: Self hosting description: Learn how to host MediaLit API --- -This guide explains how to self-host MediaLit with an S3-compatible storage provider. +This guide explains how to self-host MediaLit with an S3-compatible storage provider. Files are stored in your own buckets, and your apps, agents, the dashboard and the CLI talk to your server instead of medialit.cloud. + +To deploy on a single server with Docker Compose, use the [single-server setup](https://github.com/codelitdev/single-server-setup/tree/main/medialit). The steps below configure the storage it needs. ## Requirements 1. Two buckets: one dedicated to private files and one to public files. @@ -104,7 +106,7 @@ DISABLE_TAGGING=true ### Minio -Minio by default uses Path Style (e.g. `https://minio-api.example.com/bucket-name`) while CourseLit/AWS S3 uses Virtual Hosted Style (e.g. `https://bucket-name.minio-api.example.com`) +Minio by default uses Path Style (e.g. `https://minio-api.example.com/bucket-name`) while MediaLit/AWS S3 uses Virtual Hosted Style (e.g. `https://bucket-name.minio-api.example.com`) In order for Minio to be used as object storage backend for MediaLit, additional DNS entry and certificates need to be generated. @@ -202,3 +204,43 @@ CDN_ENDPOINT=https:// # For accessing public bucket con ``` > In the Minio setup, you cannot serve private bucket content through the CDN. + +## Set upload limits + +These variables set the largest file and the total storage per account, in bytes. "Subscribed" applies to accounts with an active subscription. The first user created from `EMAIL` has one. + +```sh +MAX_UPLOAD_SIZE_SUBSCRIBED=2147483648 # 2 GB +MAX_UPLOAD_SIZE_NOT_SUBSCRIBED=52428800 # 50 MB +MAX_STORAGE_ALLOWED_SUBSCRIBED=107374182400 # 100 GB +MAX_STORAGE_ALLOWED_NOT_SUBSCRIBED=1073741824 # 1 GB +TEMP_MEDIA_EXPIRATION_HOURS=24 # unsealed uploads are deleted after this +``` + +## Point your apps at your server + +Set `MEDIALIT_ENDPOINT` wherever you use the [Node.js SDK](/node-sdk): + +```sh +MEDIALIT_ENDPOINT=https://medialit.example.com +MEDIALIT_API_KEY=your_api_key +``` + +If your server reaches MediaLit on a different URL than browsers do, for example inside Docker, pass `publicEndpoint` to [`createSignatureHandler`](/node-sdk#createsignaturehandler). MCP clients connect to `https://medialit.example.com/mcp`, and the [CLI](/cli) logs in with `medialit login --endpoint https://medialit.example.com`. + + +## Monitor your server + +Find out that MediaLit is down before your users do. + +1. **Uptime:** point an uptime monitor such as Better Stack or UptimeRobot at `https://medialit.example.com/ready`. It returns 200 when the API is running and can reach its database, and 503 otherwise. +2. **End to end:** run the synthetic check every 10 to 15 minutes from a machine other than your MediaLit server. It uploads a tiny file, seals it, downloads it and deletes it, so it also catches storage and CDN problems that `/ready` can't see. + +```bash +MEDIALIT_APIKEY=your_monitoring_app_key \ +MEDIALIT_SERVER=https://medialit.example.com \ +MEDIALIT_SYNTHETIC_HEARTBEAT_URL=https://uptime.betterstack.com/api/v1/heartbeat/... \ + npx @medialit/integration-tests@0.1.0 synthetic +``` + +Create a separate app for monitoring and use its API key: the synthetic check only touches its own files. With a heartbeat URL from Better Stack or Healthchecks.io, you are alerted when a check fails and when checks stop arriving. diff --git a/apps/docs/content/docs/upgrade-from-v0-4-0-to-v0-5-0.mdx b/apps/docs/content/docs/upgrade-from-v0-4-0-to-v0-5-0.mdx new file mode 100644 index 00000000..0f8d123e --- /dev/null +++ b/apps/docs/content/docs/upgrade-from-v0-4-0-to-v0-5-0.mdx @@ -0,0 +1,156 @@ +--- +title: Upgrade from v0.4.0 to v0.5.0 +description: Move a self-hosted MediaLit installation from MongoDB to PostgreSQL +--- + +MediaLit v0.5.0 uses PostgreSQL instead of MongoDB. This upgrade requires a +one-time database import. Your files remain in their existing S3-compatible +buckets; the import moves MediaLit users, API keys, media metadata, media +settings, and upload signatures. + + + Stop MediaLit v0.4.0 before starting the import so that no records are + written to MongoDB during the migration. Keep MongoDB and your object + storage available until you have verified the v0.5.0 deployment. + + +The steps below use the official single-server Docker Compose setup and run the +data import from your local computer. If you manage PostgreSQL separately, use +the equivalent commands for your deployment. + +## 1. Back up the existing installation + +Back up the v0.4.0 MongoDB database and your MediaLit environment file. Keep a +copy of the current Docker Compose configuration so that you can return to +v0.4.0 if needed. + +Stop the v0.4.0 API and web containers, but leave MongoDB running and reachable +from the computer that will run the import. + +## 2. Prepare the v0.5.0 deployment + +Replace the v0.4.0 Compose configuration with the v0.5.0 configuration from the +[single-server setup](https://github.com/codelitdev/single-server-setup/tree/main/medialit) +and copy its `.env.example` to `.env`. Review every value before continuing and +set `TAG=v0.5.0` to pin both application images to this release. + +The database variables have changed: + +- `DB_CONNECTION_STRING` and `MONGODB_URI` are no longer used by the application. +- `POSTGRES_USER`, `POSTGRES_PASSWORD`, and `POSTGRES_DB` configure the bundled PostgreSQL service. +- `DATABASE_URL` is assembled by the Compose file for the API and migration containers. + +Also configure the new v0.5.0 application values, including +`BETTER_AUTH_SECRET`, `EMAIL`, `PUBLIC_API_URL`, and `WEB_ORIGIN`. Keep the +existing storage bucket names and credentials because this migration does not +copy or move stored files. + +Pull the v0.5.0 images and start PostgreSQL: + +```sh +docker compose pull +docker compose up -d postgres +``` + +Create the PostgreSQL schema with the one-time `init` service: + +```sh +docker compose run --rm init +``` + +Do not start the v0.5.0 API or web application yet. + +## 3. Prepare the importer locally + +The importer is included in the MediaLit repository and requires Bun 1.4.1. +Run these commands on your local computer: + +```sh +git clone https://github.com/codelitdev/medialit.git +cd medialit +git checkout v0.5.0 +bun install --frozen-lockfile +cp packages/scripts/.env.example packages/scripts/.env +``` + +Set the source MongoDB and destination PostgreSQL URLs in +`packages/scripts/.env`: + +```dotenv +MONGO_URL=mongodb://:@:27017/ +DATABASE_URL=postgresql://medialit:@127.0.0.1:5438/medialit +``` + +Both databases must be reachable from your local computer. Keep database ports +private. For example, when PostgreSQL is running on a remote host and its +Compose port is the default `127.0.0.1:5433`, open an SSH tunnel in a separate +terminal: + +```sh +ssh -N -L 5438:127.0.0.1:5433 @ +``` + +The example `DATABASE_URL` above then connects through local port `5438`. If +MongoDB is also only reachable from the server, use another SSH tunnel or an +equivalent private connection. Do not expose either database publicly. +Percent-encode any special characters used in database usernames or passwords +before placing them in a connection URL. + +## 4. Import MongoDB data + + + Run the importer once against a newly migrated PostgreSQL database. The + command is intended for a one-time migration and is not safe to rerun + against a database that already contains imported records. + + +From the MediaLit repository on your local computer, run: + +```sh +bun --filter @medialit/scripts import:mongo +``` + +The importer checks that the PostgreSQL schema exists before writing data. It +preserves MongoDB user IDs, public user IDs, API key secrets, and media IDs. If +a legacy record belongs to a user that no longer exists in MongoDB, the importer +skips it and reports the count instead of creating an ownerless PostgreSQL row. + +TUS upload session state is not imported. Complete any active uploads before the +maintenance window or restart them after the upgrade. Completed media library +records are imported normally. + +The command ends with a summary similar to this: + +```json +{ + "users": 5, + "media": 250, + "signatures": 4, + "skippedMedia": 0, + "skippedSignatures": 0 +} +``` + +Review any skipped-record warnings before continuing. A skipped record usually +means its MongoDB owner had already been deleted. + +If the command fails after it starts inserting rows, recreate the empty +PostgreSQL destination, run the `init` service again, fix the reported problem, +and restart the import. Do not rerun it against the partially imported database. + +## 5. Start and verify v0.5.0 + +Start the complete deployment: + +```sh +docker compose up -d +docker compose ps +``` + +Verify that the PostgreSQL, init, API, and web services are healthy. Then sign +in and confirm that users, API keys, media totals, groups, captions, and media +settings match the v0.4.0 installation. Test both a public media URL and a +private media URL before reopening the service to users. + +Keep the MongoDB backup until the upgraded installation has been running +successfully and you are satisfied with the imported data. diff --git a/apps/docs/content/docs/what-is-medialit.mdx b/apps/docs/content/docs/what-is-medialit.mdx index 44835f32..0840472c 100644 --- a/apps/docs/content/docs/what-is-medialit.mdx +++ b/apps/docs/content/docs/what-is-medialit.mdx @@ -1,33 +1,35 @@ --- -title: What is MediaLit -description: Introducing Medialit, an easy way to add file uploads to your apps. -icon: CircleHelp +title: Why MediaLit +description: What MediaLit adds on top of object storage. +icon: CircleQuestionMark --- -We believe that adding file uploads should be a no-brainer task for developers. +Object storage such as AWS S3 or Cloudflare R2 is cheap and reliable, but it only stores bytes. To use it for uploads in a real app, you also need presigned URLs, bucket policies, CORS, a CDN, thumbnails, a database of what you stored, and a way to clean up files nobody uses. MediaLit is that layer, already built. -In reality, the process is quite complex, as devs are supposed to know storage, policies, CDNs, and firewalls in order to build a decent solution. +## One place for agents, apps and humans -**Hence, we built MediaLit, where everything has been configured for you. All you need is an API key and some code snippets to add file uploads to your apps.** +Most file tools are built for one kind of user. MediaLit gives the same files three ways in: -## Philosophy +- **Apps** use the [REST API](/api/uploadMedia), the [Node.js SDK](/node-sdk) and the [React uploader](/react). +- **Agents** use the [MCP server](/mcp-server), so Claude, ChatGPT or Cursor can save a file and hand you a link. Agents that run in a terminal, such as Claude Code or Codex, can also run the [CLI](/cli). +- **Humans** use the [dashboard](/dashboard) to browse, preview and share, and the same [CLI](/cli) to upload and manage files from a terminal or a CI job. -**Batteries included**: In addition to file hosting, modern apps also require other niceties like image optimization, thumbnail generation, etc. So, we have bundled the transformation utilities with the storage API. +An agent can create a report, you can check it in the dashboard, and your app can serve it from the same URL. -**100% Self-hostable**: All parts of the ecosystem have been open-sourced. We have made sure that our docs are always updated and easy to follow. No gimmicks when it comes to being OSS. +## What you don't have to build -**Vendor-agnostic**: We try to stick to using S3 features that are available on most AWS S3-compatible providers. This allows you to easily move to a different vendor if you decide to do so. +| You need | With plain object storage | With MediaLit | +| --- | --- | --- | +| Uploads from the browser | Presigned URLs, CORS rules, a policy per bucket | A signature route and a drop-in component | +| Resumable uploads | Multipart upload logic | Built in | +| Cleaning up abandoned uploads | A database of what is used, and a job to delete the rest | Unsealed uploads are deleted after 24 hours | +| Public and private files | Bucket policies, signed URLs | `access: "public"` or `"private"` | +| Listing files with their details | `ListObjects` and your own database | Paginated listing, filtered by group or access | +| Thumbnails | A processing pipeline | Generated for images and videos | +| Upload size and storage limits | Your own accounting | Enforced on every upload | -## Why MediaLit +## Open source, and portable -We designed MediaLit with simplicity in mind. +All of MediaLit is open source, including what runs [medialit.cloud](https://medialit.cloud). Start on the hosted service, and if you outgrow it or need your data elsewhere, [run the same API yourself](/self-hosting) on AWS S3, Cloudflare R2, MinIO or any S3-compatible storage. Point `MEDIALIT_ENDPOINT` at your server and your code keeps working, because the API is the same. -Here are a few unique features: -- A simpler API for file hosting than AWS S3. -- Integrated CDN. -- Automatic image optimization (configurable). -- Automatic thumbnail generation for images and videos (configurable). - -### Serverless Apps - -MediaLit allows you to create pre-signed URLs that can be shared with serverless apps to let them upload files directly from the frontend. No more leaking keys. \ No newline at end of file +MediaLit only uses storage features that S3-compatible providers have in common, so you can also move between storage providers. diff --git a/apps/docs/mdx-components.tsx b/apps/docs/mdx-components.tsx index da6e71cf..c8ecb47e 100644 --- a/apps/docs/mdx-components.tsx +++ b/apps/docs/mdx-components.tsx @@ -1,10 +1,18 @@ import defaultMdxComponents from "fumadocs-ui/mdx"; +import { Step, Steps } from "fumadocs-ui/components/steps"; +import { Tab, Tabs } from "fumadocs-ui/components/tabs"; +import { TypeTable } from "fumadocs-ui/components/type-table"; import type { MDXComponents } from "mdx/types"; // use this function to get MDX components, you will need it for rendering MDX export function getMDXComponents(components?: MDXComponents): MDXComponents { return { ...defaultMdxComponents, + Step, + Steps, + Tab, + Tabs, + TypeTable, ...components, }; } diff --git a/apps/docs/package.json b/apps/docs/package.json index 4e681d9c..318b582b 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -9,12 +9,13 @@ "postinstall": "fumadocs-mdx" }, "dependencies": { + "@codelitdev/design-system": "0.3.0", "fumadocs-core": "16.7.1", "fumadocs-mdx": "14.2.10", "fumadocs-openapi": "^10.4.1", "fumadocs-ui": "16.7.1", - "lucide-react": "^0.487.0", - "next": "16.2.0", + "lucide-react": "^0.577.0", + "next": "16.3.6", "react": "^19.2.0", "react-dom": "^19.2.0", "shiki": "^4.0.2" @@ -27,6 +28,7 @@ "@types/react-dom": "^19.2.2", "postcss": "^8.5.3", "tailwindcss": "^4.1.4", - "typescript": "^5.8.3" + "typescript": "^5.8.3", + "vite": "^7.3.0" } -} \ No newline at end of file +} diff --git a/apps/docs/source.config.ts b/apps/docs/source.config.ts index 03c528a6..409ae13e 100644 --- a/apps/docs/source.config.ts +++ b/apps/docs/source.config.ts @@ -10,8 +10,32 @@ export const docs = defineDocs({ }, }); +// ```npm code blocks show one tab per package manager. The docs only use +// `npm install`, so translating that command is enough. +const install = (command: string) => (cmd: string) => + cmd.replace(/^npm install\b/gm, command); + export default defineConfig({ mdxOptions: { - // MDX options + remarkNpmOptions: { + // Picking a package manager once applies to every page. + persist: { id: "package-manager" }, + packageManagers: [ + { name: "npm", command: (cmd) => cmd }, + { name: "pnpm", command: install("pnpm add") }, + { + name: "yarn", + // Yarn has no `-g` flag for add. + command: (cmd) => + install("yarn add")( + cmd.replace( + /^npm install -g\b/gm, + "yarn global add", + ), + ), + }, + { name: "bun", command: install("bun add") }, + ], + }, }, }); diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile index c2b64dd5..f7909293 100644 --- a/apps/web/Dockerfile +++ b/apps/web/Dockerfile @@ -1,46 +1,37 @@ -FROM node:20-alpine -ENV PNPM_HOME="/pnpm" -ENV PATH="$PNPM_HOME:$PATH" -RUN corepack enable - +FROM oven/bun:1.4.1 AS deps WORKDIR /app -# copy pnpm specific files -COPY package.json . -COPY pnpm-lock.yaml . -COPY pnpm-workspace.yaml . - -# copy project files -COPY tsconfig.json . +COPY package.json bun.lock ./ COPY apps/web/package.json ./apps/web/package.json -COPY apps/web/app ./apps/web/app -COPY apps/web/components ./apps/web/components -COPY apps/web/lib ./apps/web/lib -COPY apps/web/models ./apps/web/models -COPY apps/web/public ./apps/web/public -COPY apps/web/utils ./apps/web/utils -COPY apps/web/auth.ts ./apps/web/auth.ts -COPY apps/web/middleware.ts ./apps/web/middleware.ts -COPY apps/web/components.json ./apps/web/components.json -COPY apps/web/next.config.js ./apps/web/next.config.js -COPY apps/web/postcss.config.js ./apps/web/postcss.config.js -COPY apps/web/tailwind.config.ts ./apps/web/tailwind.config.ts -COPY apps/web/tsconfig.json ./apps/web/tsconfig.json +COPY packages/models/package.json ./packages/models/package.json + +RUN bun install --frozen-lockfile --filter @medialit/web +FROM deps AS builder + +COPY tsconfig.json ./ +COPY apps/web ./apps/web COPY packages/models ./packages/models -COPY packages/utils ./packages/utils -COPY packages/medialit ./packages/medialit -RUN pnpm install +ENV NEXT_TELEMETRY_DISABLED=1 -# RUN pnpm build -RUN pnpm --filter=@medialit/utils build -RUN pnpm --filter=@medialit/models build -RUN pnpm --filter=medialit build -RUN pnpm --filter=@medialit/web build +RUN bun --filter @medialit/web build -ENV NODE_ENV production +FROM node:22-alpine AS runner +WORKDIR /app -EXPOSE 3000 +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 +ENV HOSTNAME=0.0.0.0 +ENV PORT=3000 + +RUN addgroup --system --gid 1001 nodejs \ + && adduser --system --uid 1001 --ingroup nodejs nextjs -CMD ["pnpm", "--filter=@medialit/web", "start"] \ No newline at end of file +COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static +COPY --from=builder --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public + +USER nextjs +EXPOSE 3000 +CMD ["node", "apps/web/server.js"] diff --git a/apps/web/app/account/billing/action.ts b/apps/web/app/account/billing/action.ts index c74ee6aa..9c563431 100644 --- a/apps/web/app/account/billing/action.ts +++ b/apps/web/app/account/billing/action.ts @@ -1,126 +1,57 @@ "use server"; -import { Session } from "@/auth"; -import connectToDatabase from "@/lib/connect-db"; -import UserModel from "@/models/user"; -import { LEMONSQUEEZY_API_KEY } from "@/lib/constants"; import { auth } from "@/auth"; -import { User } from "@medialit/models"; -import { error } from "@/utils/logger"; +import { serverApi } from "@/lib/server-api"; -export async function cancelSubscription( - prevState: Record, - formData: FormData, +async function postBilling( + path: string, + body: Record = {}, ): Promise<{ success: boolean; error?: string; + url?: string; }> { try { - if (!LEMONSQUEEZY_API_KEY) { - throw new Error("Lemon API key not found"); - } - - const session: Session | null = await auth(); - if (!session || !session.user) { + const session = await auth(); + if (!session?.user) { throw new Error("Unauthorized"); } - - await connectToDatabase(); - - const user: User | null = await UserModel.findOne({ - email: session.user.email, + const response = await serverApi(path, { + method: "POST", + body: JSON.stringify(body), }); + const data = await response.json().catch(() => ({})); + if (!response.ok) { + return { + success: false, + error: data.error || "Some error occurred", + }; + } + const url = data.checkoutUrl || data.portalUrl; + return { success: true, url }; + } catch (err: any) { + return { success: false, error: err.message }; + } +} - if (!user) { - throw new Error("Unauthorized"); - } +export async function startProCheckout(interval: "month" | "year") { + return postBilling("/api/account/billing/checkout", { interval }); +} - const response = await fetch( - `https://api.lemonsqueezy.com/v1/subscriptions/${user.subscriptionId}`, - { - method: "DELETE", - headers: { - "Content-Type": "application/vnd.api+json", - Accept: "application/vnd.api+json", - Authorization: `Bearer ${LEMONSQUEEZY_API_KEY}`, - }, - }, - ); - if (response.ok) { - const resp = await response.json(); - return { success: true }; - } +export async function openBillingPortal() { + return postBilling("/api/account/billing/portal"); +} - throw new Error("Some error occurred"); - } catch (error: any) { - return { success: false, error: error.message }; - } +export async function cancelSubscription( + prevState: Record, + formData: FormData, +) { + return postBilling("/api/account/billing/cancel"); } export async function resumeSubscription( prevState: Record, formData: FormData, -): Promise<{ - success: boolean; - error?: string; -}> { - try { - if (!LEMONSQUEEZY_API_KEY) { - throw new Error("Lemon API key not found"); - } - - const session: Session | null = await auth(); - if (!session || !session.user) { - throw new Error("Unauthorized"); - } - - await connectToDatabase(); - - const user: User | null = await UserModel.findOne({ - email: session.user.email, - }); - - if (!user) { - throw new Error("Unauthorized"); - } - - const response = await fetch( - `https://api.lemonsqueezy.com/v1/subscriptions/${user.subscriptionId}`, - { - method: "PATCH", - headers: { - "Content-Type": "application/vnd.api+json", - Accept: "application/vnd.api+json", - Authorization: `Bearer ${LEMONSQUEEZY_API_KEY}`, - }, - body: JSON.stringify({ - data: { - type: "subscriptions", - id: user.subscriptionId, - attributes: { - cancelled: false, - }, - }, - }), - }, - ); - const resp = await response.json(); - - if (response.ok) { - return { success: true }; - } - - error(`Error in resuming subscription`, { - userId: user.subscriptionId, - apiResponse: resp, - statusCode: response.status, - }); - return { - success: false, - error: "Some error occurred while resuming subscription. Try again in a while.", - }; - } catch (err: any) { - error(`Error in resuming subscription`, err.stack); - return { success: false, error: err.message }; - } +) { + return postBilling("/api/account/billing/resume"); } diff --git a/apps/web/app/account/billing/dodo-checkout-button.tsx b/apps/web/app/account/billing/dodo-checkout-button.tsx new file mode 100644 index 00000000..eea6aa02 --- /dev/null +++ b/apps/web/app/account/billing/dodo-checkout-button.tsx @@ -0,0 +1,48 @@ +"use client"; + +import { useState } from "react"; +import { Button } from "@/components/ui/button"; +import { useToast } from "@/components/ui/use-toast"; +import { openBillingPortal, startProCheckout } from "./action"; + +export default function DodoCheckoutButton({ + mode, + interval, + className, + children, +}: { + mode: "checkout" | "portal"; + interval?: "month" | "year"; + className?: string; + children: React.ReactNode; +}) { + const { toast } = useToast(); + const [pending, setPending] = useState(false); + + return ( + + ); +} diff --git a/apps/web/app/account/billing/layout.tsx b/apps/web/app/account/billing/layout.tsx index af49778a..58f86d8f 100644 --- a/apps/web/app/account/billing/layout.tsx +++ b/apps/web/app/account/billing/layout.tsx @@ -5,10 +5,8 @@ import { BreadcrumbLink, BreadcrumbList, } from "@/components/ui/breadcrumb"; -import Link from "next/link"; import { ReactNode } from "react"; import { redirect } from "next/navigation"; -import Script from "next/script"; import { auth } from "@/auth"; const SchoolDetailsLayout = async ({ children }: { children: ReactNode }) => { @@ -29,11 +27,6 @@ const SchoolDetailsLayout = async ({ children }: { children: ReactNode }) => {

Billing

- - - - - + ); } diff --git a/apps/web/auth.ts b/apps/web/auth.ts index ae316063..0a867bbc 100644 --- a/apps/web/auth.ts +++ b/apps/web/auth.ts @@ -2,12 +2,7 @@ import { cookies } from "next/headers"; import { redirect } from "next/navigation"; -import { - ACCESS_TOKEN_COOKIE, - REFRESH_TOKEN_COOKIE, - USER_COOKIE, - revokeRefreshToken, -} from "@/lib/oauth-session"; +import { apiBase } from "@/lib/server-api"; export interface SessionUser { id: string; @@ -17,34 +12,53 @@ export interface SessionUser { export interface Session { user: SessionUser; - accessToken: string; } export async function auth(): Promise { const cookieStore = await cookies(); - const accessToken = cookieStore.get(ACCESS_TOKEN_COOKIE)?.value; - const userJson = cookieStore.get(USER_COOKIE)?.value; + const cookie = cookieStore.toString(); + if (!cookie.includes("session_token")) { + return null; + } - if (!accessToken || !userJson) { + const response = await fetch(`${apiBase()}/api/auth/get-session`, { + headers: { cookie }, + cache: "no-store", + }); + if (!response.ok) { return null; } - try { - const user = JSON.parse(userJson) as SessionUser; - return { - user, - accessToken, - }; - } catch { + const data = await response.json(); + if (!data?.user?.id || !data?.user?.email) { return null; } + + return { + user: { + id: data.user.id, + email: data.user.email, + name: data.user.name, + }, + }; } export async function signOut() { const cookieStore = await cookies(); - await revokeRefreshToken(cookieStore.get(REFRESH_TOKEN_COOKIE)?.value); - cookieStore.delete(ACCESS_TOKEN_COOKIE); - cookieStore.delete(REFRESH_TOKEN_COOKIE); - cookieStore.delete(USER_COOKIE); + await fetch(`${apiBase()}/api/auth/sign-out`, { + method: "POST", + headers: { + cookie: cookieStore.toString(), + "content-type": "application/json", + }, + body: "{}", + cache: "no-store", + }).catch(() => undefined); + + for (const cookie of cookieStore.getAll()) { + if (cookie.name.includes("medialit")) { + cookieStore.delete(cookie.name); + } + } redirect("/login"); } diff --git a/apps/web/components/Pricing.tsx b/apps/web/components/Pricing.tsx index 7c512e2b..d3de6da4 100644 --- a/apps/web/components/Pricing.tsx +++ b/apps/web/components/Pricing.tsx @@ -45,13 +45,31 @@ const PricingPane = ({ isSecondary = false, }: PricingPaneProps) => { return ( -
+
{name}

{description}

- ${price} - /month +
+ + ${price} + + + {" "} + /month + +
+ {name === "Pro" && ( +
+ + $100 + + + {" "} + /year + +
+ )}
{features.map((feature) => ( diff --git a/apps/web/lib/apikey-handlers.ts b/apps/web/lib/apikey-handlers.ts deleted file mode 100644 index 31f862af..00000000 --- a/apps/web/lib/apikey-handlers.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { Apikey } from "@medialit/models"; -import ApikeyModel from "@/models/apikey"; -import { getUniqueId } from "@medialit/utils"; -import mongoose from "mongoose"; - -export async function getApiKeysByUserId( - userId: mongoose.Types.ObjectId, - keyId?: string, -): Promise { - let result: Apikey[] | null; - const projections = { - _id: 0, - name: 1, - // key: 1, - httpReferrers: 1, - ipAddresses: 1, - default: 1, - createdAt: 1, - updatedAt: 1, - keyId: 1, - }; - - const query: Record = { - userId, - deleted: { $ne: true }, - }; - - if (keyId) { - query.keyId = keyId; - } - - return await ApikeyModel.find(query, projections); -} - -export async function getApikeyFromKeyId( - userId: mongoose.Types.ObjectId, - keyId: string, -): Promise { - return (await ApikeyModel.findOne({ - keyId, - userId, - deleted: { $ne: true }, - }).lean()) as Apikey | null; -} - -export async function createApiKey( - userId: mongoose.Types.ObjectId, - name: string, -): Promise { - return await ApikeyModel.create({ - name, - key: getUniqueId(), - userId, - }); -} - -export async function deleteApiKey( - userId: mongoose.Types.ObjectId, - keyId: string, -) { - const key = await ApikeyModel.findOne({ keyId, userId }); - if (key && key.default) { - throw new Error("Default API key cannot be deleted"); - } - return await ApikeyModel.updateOne( - { - keyId, - userId, - }, - { $set: { deleted: true } }, - ); -} - -export async function editApiKey({ - userId, - name, - newName, -}: { - userId: mongoose.Types.ObjectId; - name: string; - newName: string; -}) { - const query = { userId, name }; - const editedApiKey = await ApikeyModel.updateOne(query, { - $set: { name: newName }, - }); - - return editedApiKey; -} - -export async function getApikeyByUserId({ - userId, - keyId, -}: { - userId: mongoose.Types.ObjectId; - keyId: string; -}): Promise { - const apikeys = await getApiKeysByUserId(userId, keyId); - if (!apikeys || apikeys.length === 0 || apikeys[0].keyId !== keyId) { - throw new Error("Apikey not found"); - } - - return await getApikeyFromKeyId(userId, keyId); -} diff --git a/apps/web/lib/connect-db.ts b/apps/web/lib/connect-db.ts deleted file mode 100644 index 9b99b55e..00000000 --- a/apps/web/lib/connect-db.ts +++ /dev/null @@ -1,18 +0,0 @@ -import mongoose from "mongoose"; - -export default async function connectToDatabase(): Promise { - if (mongoose.connection.readyState >= 1) { - return mongoose.connection.getClient(); - } - - const options = { - useNewUrlParser: true, - serverSelectionTimeoutMS: 5000, - }; - - const dbConnection = await mongoose.connect( - process.env.MONGODB_URI || "", - options, - ); - return dbConnection.connection.getClient(); -} diff --git a/apps/web/lib/constants.ts b/apps/web/lib/constants.ts index 9150d2a3..fdc66988 100644 --- a/apps/web/lib/constants.ts +++ b/apps/web/lib/constants.ts @@ -1,6 +1 @@ export const SITE_NAME = process.env.SITE_NAME || "MediaLit"; -export const LEMONSQUEEZY_STORE_ID = process.env.LEMONSQUEEZY_STORE_ID; -export const LEMONSQUEEZY_PRODUCT_ID = process.env.LEMONSQUEEZY_PRODUCT_ID; -export const LEMONSQUEEZY_WEBHOOK_SECRET = - process.env.LEMONSQUEEZY_WEBHOOK_SECRET; -export const LEMONSQUEEZY_API_KEY = process.env.LEMONSQUEEZY_API_KEY; diff --git a/apps/web/lib/get-medialit-client.ts b/apps/web/lib/get-medialit-client.ts deleted file mode 100644 index d5d4157a..00000000 --- a/apps/web/lib/get-medialit-client.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { MediaLit } from "medialit"; - -export function getMediaLitClient(apiKey: string) { - return new MediaLit({ - apiKey, - endpoint: process.env.API_SERVER, - }); -} diff --git a/apps/web/lib/oauth-session.ts b/apps/web/lib/oauth-session.ts deleted file mode 100644 index a85117f9..00000000 --- a/apps/web/lib/oauth-session.ts +++ /dev/null @@ -1,72 +0,0 @@ -export const ACCESS_TOKEN_COOKIE = "session_access_token"; -export const REFRESH_TOKEN_COOKIE = "session_refresh_token"; -export const USER_COOKIE = "session_user"; - -export const ACCESS_TOKEN_REFRESH_SKEW_SECONDS = 60; -export const DEFAULT_ACCESS_TOKEN_MAX_AGE_SECONDS = 15 * 60; -export const REFRESH_TOKEN_MAX_AGE_SECONDS = 30 * 24 * 60 * 60; - -type TokenCookieOptions = { - httpOnly: boolean; - secure: boolean; - sameSite: "lax"; - path: string; - maxAge: number; -}; - -export function tokenCookieOptions(maxAge: number): TokenCookieOptions { - return { - httpOnly: true, - secure: process.env.NODE_ENV === "production", - sameSite: "lax", - path: "/", - maxAge, - }; -} - -function decodeBase64Url(value: string): string { - const base64 = value.replace(/-/g, "+").replace(/_/g, "/"); - const padded = base64.padEnd( - base64.length + ((4 - (base64.length % 4)) % 4), - "=", - ); - - return atob(padded); -} - -export function getJwtExpiresAtSeconds(token: string): number | null { - const [, payload] = token.split("."); - if (!payload) return null; - - try { - const parsed = JSON.parse(decodeBase64Url(payload)); - return typeof parsed.exp === "number" ? parsed.exp : null; - } catch { - return null; - } -} - -export function shouldRefreshAccessToken( - token: string, - nowSeconds = Math.floor(Date.now() / 1000), -): boolean { - const expiresAt = getJwtExpiresAtSeconds(token); - if (!expiresAt) return true; - return expiresAt - nowSeconds <= ACCESS_TOKEN_REFRESH_SKEW_SECONDS; -} - -export async function revokeRefreshToken(refreshToken?: string): Promise { - if (!refreshToken || !process.env.API_SERVER) return; - - try { - await fetch(`${process.env.API_SERVER}/oauth/revoke`, { - method: "POST", - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - body: new URLSearchParams({ token: refreshToken }).toString(), - }); - } catch { - return; - } -} diff --git a/apps/web/lib/server-api.ts b/apps/web/lib/server-api.ts new file mode 100644 index 00000000..04228083 --- /dev/null +++ b/apps/web/lib/server-api.ts @@ -0,0 +1,23 @@ +import { cookies } from "next/headers"; + +export function apiBase() { + return ( + process.env.API_SERVER || + process.env.PUBLIC_API_URL || + "http://127.0.0.1:8000" + ).replace(/\/$/, ""); +} + +export async function serverApi(path: string, init?: RequestInit) { + const cookieStore = await cookies(); + const headers = new Headers(init?.headers); + headers.set("cookie", cookieStore.toString()); + if (init?.body && !headers.has("content-type")) { + headers.set("content-type", "application/json"); + } + return fetch(`${apiBase()}${path}`, { + ...init, + headers, + cache: "no-store", + }); +} diff --git a/apps/web/lib/user-handlers.ts b/apps/web/lib/user-handlers.ts deleted file mode 100644 index e20e9df1..00000000 --- a/apps/web/lib/user-handlers.ts +++ /dev/null @@ -1,16 +0,0 @@ -import UserModel from "@/models/user"; -import { User } from "@medialit/models"; -import mongoose from "mongoose"; - -type UserWithId = User & { _id: mongoose.Types.ObjectId }; - -export async function getUserFromSession( - session: { user?: { email?: string | null } } | null, -): Promise { - if (!session || !session.user || !session.user.email) return null; - const dbUser: UserWithId | null = (await UserModel.findOne({ - email: session.user.email, - }).lean()) as UserWithId | null; - - return dbUser; -} diff --git a/apps/web/middleware.ts b/apps/web/middleware.ts deleted file mode 100644 index 0f0724ec..00000000 --- a/apps/web/middleware.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { NextResponse } from "next/server"; -import type { NextRequest } from "next/server"; -import { - ACCESS_TOKEN_COOKIE, - DEFAULT_ACCESS_TOKEN_MAX_AGE_SECONDS, - REFRESH_TOKEN_COOKIE, - REFRESH_TOKEN_MAX_AGE_SECONDS, - USER_COOKIE, - shouldRefreshAccessToken, - tokenCookieOptions, -} from "@/lib/oauth-session"; - -async function refreshSession(request: NextRequest) { - const refreshToken = request.cookies.get(REFRESH_TOKEN_COOKIE)?.value; - if (!refreshToken || !process.env.API_SERVER) return null; - - try { - const tokenResponse = await fetch( - `${process.env.API_SERVER}/oauth/token`, - { - method: "POST", - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - body: new URLSearchParams({ - grant_type: "refresh_token", - refresh_token: refreshToken, - client_id: "web-client", - }).toString(), - }, - ); - - if (!tokenResponse.ok) return null; - - const tokenData = await tokenResponse.json(); - if (!tokenData.access_token || !tokenData.refresh_token) return null; - - return { - accessToken: String(tokenData.access_token), - refreshToken: String(tokenData.refresh_token), - expiresIn: - Number(tokenData.expires_in) || - DEFAULT_ACCESS_TOKEN_MAX_AGE_SECONDS, - }; - } catch { - return null; - } -} - -function redirectToLogin(request: NextRequest) { - const url = request.nextUrl.clone(); - url.pathname = "/login"; - const response = NextResponse.redirect(url); - response.cookies.delete(ACCESS_TOKEN_COOKIE); - response.cookies.delete(REFRESH_TOKEN_COOKIE); - response.cookies.delete(USER_COOKIE); - return response; -} - -function setRefreshedTokenCookies( - response: NextResponse, - refreshed: { - accessToken: string; - refreshToken: string; - expiresIn: number; - }, -) { - response.cookies.set( - ACCESS_TOKEN_COOKIE, - refreshed.accessToken, - tokenCookieOptions(refreshed.expiresIn), - ); - response.cookies.set( - REFRESH_TOKEN_COOKIE, - refreshed.refreshToken, - tokenCookieOptions(REFRESH_TOKEN_MAX_AGE_SECONDS), - ); -} - -export async function middleware(request: NextRequest) { - const accessToken = request.cookies.get(ACCESS_TOKEN_COOKIE)?.value; - const refreshToken = request.cookies.get(REFRESH_TOKEN_COOKIE)?.value; - const url = request.nextUrl.clone(); - - const isLoginPage = url.pathname.startsWith("/login"); - const isCallbackPage = url.pathname.startsWith( - "/api/auth/callback/medialit", - ); - const isSignoutPage = url.pathname.startsWith("/api/auth/signout"); - const isStaticAsset = - url.pathname.includes(".") || - url.pathname.startsWith("/_next") || - url.pathname.startsWith("/api/cleanup"); - - if (isStaticAsset) { - return NextResponse.next(); - } - - if (!accessToken && !isCallbackPage && !isSignoutPage) { - if (refreshToken) { - const refreshed = await refreshSession(request); - if (refreshed) { - if (isLoginPage) { - url.pathname = "/"; - const response = NextResponse.redirect(url); - setRefreshedTokenCookies(response, refreshed); - return response; - } - - const response = NextResponse.next(); - setRefreshedTokenCookies(response, refreshed); - return response; - } - } - - if (!isLoginPage) { - return redirectToLogin(request); - } - } - - if (accessToken && isLoginPage) { - url.pathname = "/"; - return NextResponse.redirect(url); - } - - if ( - accessToken && - !isLoginPage && - !isCallbackPage && - !isSignoutPage && - shouldRefreshAccessToken(accessToken) - ) { - const refreshed = await refreshSession(request); - if (!refreshed) return redirectToLogin(request); - - const response = NextResponse.next(); - setRefreshedTokenCookies(response, refreshed); - return response; - } - - return NextResponse.next(); -} - -export const config = { - matcher: [ - "/((?!api/cleanup|_next/static|_next/image|favicon.ico|icon.svg).*)", - ], -}; diff --git a/apps/web/models/apikey.ts b/apps/web/models/apikey.ts deleted file mode 100644 index fa634954..00000000 --- a/apps/web/models/apikey.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { ApikeySchema } from "@medialit/models"; -import mongoose from "mongoose"; - -export default mongoose.models?.Apikey || - mongoose.model("Apikey", ApikeySchema); diff --git a/apps/web/models/log.ts b/apps/web/models/log.ts deleted file mode 100644 index 7098e2b5..00000000 --- a/apps/web/models/log.ts +++ /dev/null @@ -1,18 +0,0 @@ -import mongoose from "mongoose"; - -const LogSchema = new mongoose.Schema( - { - severity: { - type: String, - required: true, - enum: ["error", "info", "warn"], - }, - message: { type: String, required: true }, - metadata: { type: mongoose.Schema.Types.Mixed }, - }, - { - timestamps: true, - }, -); - -export default mongoose.models.Log || mongoose.model("Log", LogSchema); diff --git a/apps/web/models/media.ts b/apps/web/models/media.ts deleted file mode 100644 index f615f619..00000000 --- a/apps/web/models/media.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { MediaSchema } from "@medialit/models"; -import mongoose from "mongoose"; - -export default mongoose.models?.Media || mongoose.model("Media", MediaSchema); diff --git a/apps/web/models/severity.ts b/apps/web/models/severity.ts deleted file mode 100644 index d1045658..00000000 --- a/apps/web/models/severity.ts +++ /dev/null @@ -1,7 +0,0 @@ -enum Severity { - ERROR = "error", - INFO = "info", - WARN = "warn", -} - -export default Severity; diff --git a/apps/web/models/user.ts b/apps/web/models/user.ts deleted file mode 100644 index 8585adcc..00000000 --- a/apps/web/models/user.ts +++ /dev/null @@ -1,4 +0,0 @@ -import mongoose from "mongoose"; -import { UserSchema } from "@medialit/models"; - -export default mongoose.models?.User || mongoose.model("User", UserSchema); diff --git a/apps/web/next.config.js b/apps/web/next.config.js index 58f1cb58..4e695e3f 100644 --- a/apps/web/next.config.js +++ b/apps/web/next.config.js @@ -1,5 +1,7 @@ /** @type {import('next').NextConfig} */ const nextConfig = { + output: "standalone", + transpilePackages: ["@medialit/models", "@codelitdev/observability"], images: { remotePatterns: [ { diff --git a/apps/web/package.json b/apps/web/package.json index 4563de23..efc5d907 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -6,11 +6,11 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "eslint ." }, "dependencies": { - "@medialit/models": "workspace:^", - "@medialit/utils": "workspace:^", + "@codelitdev/observability": "0.3.0", + "@medialit/models": "workspace:*", "@radix-ui/colors": "^3.0.0", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", @@ -27,12 +27,11 @@ "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", "lucide-react": "^0.487.0", - "medialit": "workspace:^", - "mongoose": "^8.19.3", - "next": "^15.5.7", + "next": "16.3.6", + "posthog-js": "^1.200.0", "next-themes": "^0.2.1", - "react": "19.2.0", - "react-dom": "19.2.0", + "react": "19.2.8", + "react-dom": "19.2.8", "react-icons": "^4.11.0", "sonner": "^1.3.1", "tailwind-merge": "^2.2.0", diff --git a/apps/web/proxy.ts b/apps/web/proxy.ts new file mode 100644 index 00000000..d742ba64 --- /dev/null +++ b/apps/web/proxy.ts @@ -0,0 +1,98 @@ +import { NextResponse } from "next/server"; +import type { NextRequest } from "next/server"; + +const SESSION_COOKIES = [ + "medialit.session_token", + "__Secure-medialit.session_token", +]; + +type SessionCheck = "valid" | "invalid" | "unavailable"; + +function apiBase() { + return ( + process.env.API_SERVER || + process.env.PUBLIC_API_URL || + "http://127.0.0.1:8000" + ).replace(/\/$/, ""); +} + +function hasSessionCookie(request: NextRequest) { + return SESSION_COOKIES.some((name) => request.cookies.get(name)?.value); +} + +function clearSessionCookies(request: NextRequest, response: NextResponse) { + for (const cookie of request.cookies.getAll()) { + if (!cookie.name.includes("medialit")) continue; + response.cookies.set(cookie.name, "", { + path: "/", + maxAge: 0, + secure: cookie.name.startsWith("__Secure-"), + }); + } + return response; +} + +// Cookie presence is not a session. A dead cookie used to send /login back +// to /, and the page redirect answered with a meta refresh, so the browser +// reloaded forever. +async function checkSession(request: NextRequest): Promise { + try { + const response = await fetch(`${apiBase()}/api/auth/get-session`, { + headers: { cookie: request.headers.get("cookie") ?? "" }, + cache: "no-store", + }); + if (!response.ok) { + return response.status >= 500 ? "unavailable" : "invalid"; + } + const data = await response.json(); + return data?.user?.id && data?.user?.email ? "valid" : "invalid"; + } catch { + return "unavailable"; + } +} + +export async function proxy(request: NextRequest) { + const { pathname } = request.nextUrl; + const isPublic = + pathname.startsWith("/api/") || + pathname.startsWith("/payment/webhook") || + pathname.startsWith("/_next") || + pathname.includes("."); + + if (isPublic) { + return NextResponse.next(); + } + + const check = hasSessionCookie(request) + ? await checkSession(request) + : "invalid"; + + if (pathname.startsWith("/login")) { + if (check === "valid") { + return NextResponse.redirect(new URL("/", request.url)); + } + const response = NextResponse.next(); + if (check === "invalid" && hasSessionCookie(request)) { + clearSessionCookies(request, response); + } + return response; + } + + if (check !== "valid") { + const url = request.nextUrl.clone(); + url.pathname = "/login"; + const response = NextResponse.redirect(url); + if (check === "invalid" && hasSessionCookie(request)) { + clearSessionCookies(request, response); + } + return response; + } + + return NextResponse.next(); +} + +export const config = { + matcher: [ + "/((?!api/cleanup|_next/static|_next/image|favicon.ico|icon.svg).*)", + ], +}; diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index 4fe2bdef..0002c2e4 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -11,18 +15,30 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve", + "jsx": "react-jsx", "incremental": true, - "types": ["json-schema"], + "types": [ + "json-schema" + ], "plugins": [ { "name": "next" } ], "paths": { - "@/*": ["./*"] + "@/*": [ + "./*" + ] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] } diff --git a/apps/web/utils/logger.ts b/apps/web/utils/logger.ts deleted file mode 100644 index d2977549..00000000 --- a/apps/web/utils/logger.ts +++ /dev/null @@ -1,39 +0,0 @@ -import connectToDatabase from "@/lib/connect-db"; -import Log from "@/models/log"; -import Severity from "@/models/severity"; - -export async function error( - message: string, - metadata?: Record, -): Promise { - await connectToDatabase(); - await Log.create({ - severity: Severity.ERROR, - message, - metadata, - }); -} - -export async function info( - message: string, - metadata?: Record, -): Promise { - await connectToDatabase(); - await Log.create({ - severity: Severity.INFO, - message, - metadata, - }); -} - -export async function warn( - message: string, - metadata?: Record, -): Promise { - await connectToDatabase(); - await Log.create({ - severity: Severity.WARN, - message, - metadata, - }); -} diff --git a/bun.lock b/bun.lock new file mode 100644 index 00000000..354f68ee --- /dev/null +++ b/bun.lock @@ -0,0 +1,4935 @@ +{ + "lockfileVersion": 3, + "configVersion": 1, + "workspaces": { + "": { + "name": "medialit", + "devDependencies": { + "@changesets/cli": "^2.28.1", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "^9.24.0", + "@next/eslint-plugin-next": "^15.2.4", + "@typescript-eslint/eslint-plugin": "^8.29.1", + "eslint": "^9.39.5", + "eslint-config-next": "15.2.4", + "eslint-plugin-react": "^7.37.5", + "globals": "^16.0.0", + "husky": "^9.1.7", + "lint-staged": "^15.5.0", + "prettier": "^3.5.3", + "typescript": "^5.8.3", + "typescript-eslint": "^8.29.1", + }, + }, + "apps/api": { + "name": "@medialit/api", + "version": "0.3.1", + "dependencies": { + "@aws-sdk/client-s3": "^3.1146.0", + "@aws-sdk/cloudfront-signer": "^3.1138.0", + "@aws-sdk/s3-request-presigner": "^3.1146.0", + "@better-auth/cimd": "1.7.7", + "@better-auth/oauth-provider": "1.7.7", + "@codelitdev/billing": "0.3.0", + "@codelitdev/mcp-server-kit": "0.3.0", + "@codelitdev/oauth-server-kit": "0.3.0", + "@codelitdev/observability": "0.3.0", + "@codelitdev/platform": "0.3.0", + "@medialit/images": "workspace:*", + "@medialit/models": "workspace:*", + "@medialit/thumbnail": "workspace:*", + "@medialit/utils": "workspace:*", + "@modelcontextprotocol/sdk": "1.30.0", + "@tus/file-store": "^2.1.1", + "@tus/server": "^2.4.5", + "better-auth": "1.7.7", + "cors": "^2.8.5", + "dodopayments": "2.48.0", + "dotenv": "^17.2.3", + "drizzle-orm": "0.45.2", + "express": "4.21.2", + "express-fileupload": "^1.5.2", + "express-rate-limit": "^7.5.0", + "joi": "^17.13.7", + "joi-to-swagger": "^6.2.0", + "jsonwebtoken": "^9.0.2", + "nodemailer": "^6.10.0", + "pg": "8.23.0", + "swagger-ui-express": "^5.0.1", + "zod": "3.25.76", + }, + "devDependencies": { + "@codelitdev/platform-conformance": "0.3.0", + "@electric-sql/pglite": "0.5.8", + "@types/cors": "^2.8.12", + "@types/express": "^4.17.20", + "@types/express-fileupload": "^1.2.2", + "@types/jsonwebtoken": "^9.0.10", + "@types/node": "^22.14.1", + "@types/nodemailer": "^6.4.17", + "@types/pg": "8.23.1", + "@types/swagger-ui-express": "^4.1.8", + "drizzle-kit": "0.31.10", + "swagger-autogen": "^2.23.7", + "tsx": "^4.20.6", + "typescript": "5.9.3", + }, + }, + "apps/docs": { + "name": "@medialit/docs", + "version": "0.0.0", + "dependencies": { + "@codelitdev/design-system": "0.3.0", + "fumadocs-core": "16.7.1", + "fumadocs-mdx": "14.2.10", + "fumadocs-openapi": "^10.4.1", + "fumadocs-ui": "16.7.1", + "lucide-react": "^0.577.0", + "next": "16.3.6", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "shiki": "^4.0.2", + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.1.4", + "@types/mdx": "^2.0.13", + "@types/node": "22.14.1", + "@types/react": "^19.2.2", + "@types/react-dom": "^19.2.2", + "postcss": "^8.5.3", + "tailwindcss": "^4.1.4", + "typescript": "^5.8.3", + "vite": "^7.3.0", + }, + }, + "apps/web": { + "name": "@medialit/web", + "version": "0.1.2", + "dependencies": { + "@codelitdev/observability": "0.3.0", + "@medialit/models": "workspace:*", + "@radix-ui/colors": "^3.0.0", + "@radix-ui/react-avatar": "^1.0.4", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-progress": "^1.1.4", + "@radix-ui/react-separator": "^1.0.3", + "@radix-ui/react-slot": "^1.2.0", + "@radix-ui/react-switch": "^1.0.3", + "@radix-ui/react-tabs": "^1.1.8", + "@radix-ui/react-toast": "^1.1.5", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.0", + "lucide-react": "^0.487.0", + "next": "16.3.6", + "next-themes": "^0.2.1", + "posthog-js": "^1.200.0", + "react": "19.2.8", + "react-dom": "19.2.8", + "react-icons": "^4.11.0", + "sonner": "^1.3.1", + "tailwind-merge": "^2.2.0", + "tailwindcss-animate": "^1.0.7", + "zod": "^3.22.4", + }, + "devDependencies": { + "@types/json-schema": "^7.0.15", + "@types/node": "20.10.6", + "@types/react": "19.2.2", + "@types/react-dom": "19.2.2", + "autoprefixer": "^10.4.17", + "eslint": "^8", + "eslint-config-next": "15.2.4", + "postcss": "^8.4.33", + "tailwindcss": "^3.4.1", + "typescript": "^5", + }, + }, + "examples/express": { + "name": "@example/express", + "version": "0.1.0", + "dependencies": { + "cors": "^2.8.5", + "express": "^4.21.2", + "medialit": "^0.2.0", + "multer": "^1.4.5-lts.2", + }, + }, + "examples/next-app-router": { + "name": "@example/next-app-router", + "version": "0.1.3", + "dependencies": { + "@medialit/react": "workspace:^", + "lucide-react": "^0.487.0", + "medialit": "workspace:^", + "next": "16.3.6", + "react": "^19.0.0", + "react-dom": "^19.0.0", + }, + "devDependencies": { + "@eslint/eslintrc": "^3", + "@tailwindcss/postcss": "^4", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "15.3.1", + "tailwindcss": "^4", + "typescript": "^5", + }, + }, + "packages/cli": { + "name": "@medialit/cli", + "version": "0.1.0", + "bin": { + "medialit": "dist/index.mjs", + }, + "dependencies": { + "@medialit/uploader": "workspace:^", + "medialit": "workspace:^", + "mime-types": "^3.0.2", + }, + "devDependencies": { + "@types/mime-types": "^3.0.1", + "@types/node": "^20.0.0", + "tsup": "^8.0.0", + "tsx": "^4.19.3", + "typescript": "^5.0.0", + }, + }, + "packages/images": { + "name": "@medialit/images", + "version": "0.0.26", + "devDependencies": { + "@types/node": "^20.8.7", + "typescript": "^5.2.2", + }, + }, + "packages/integration-tests": { + "name": "@medialit/integration-tests", + "version": "0.1.0", + "bin": { + "medialit-integration-tests": "dist/bin.mjs", + }, + "devDependencies": { + "@types/node": "^20.0.0", + "tsup": "^8.0.0", + "typescript": "^5.0.0", + }, + }, + "packages/medialit": { + "name": "medialit", + "version": "0.2.0", + "dependencies": { + "mime-types": "^3.0.2", + }, + "devDependencies": { + "@types/mime-types": "^3.0.1", + "@types/node": "^20.0.0", + "tsup": "^8.0.0", + "tsx": "^4.19.3", + "typescript": "^5.0.0", + }, + }, + "packages/models": { + "name": "@medialit/models", + "version": "0.1.0", + "devDependencies": { + "typescript": "^5.9.3", + }, + }, + "packages/react": { + "name": "@medialit/react", + "version": "0.1.0", + "dependencies": { + "@medialit/uploader": "workspace:^", + }, + "devDependencies": { + "@types/node": "^20.0.0", + "@types/react": "^19", + "react": "^19.0.0", + "tsup": "^8.0.0", + "tsx": "^4.19.3", + "typescript": "^5.0.0", + }, + "peerDependencies": { + "react": ">=18", + }, + }, + "packages/scripts": { + "name": "@medialit/scripts", + "version": "0.0.1", + "dependencies": { + "dotenv": "^16.0.0", + "mongodb": "^6.16.0", + "pg": "8.23.0", + }, + "devDependencies": { + "@types/node": "^20.0.0", + "typescript": "^5.0.0", + }, + }, + "packages/thumbnail": { + "name": "@medialit/thumbnail", + "version": "0.0.28", + "bin": { + "generate-thumbnail": "./bin/generate-thumbnail.sh", + }, + "devDependencies": { + "@types/node": "^20.8.7", + "jest": "^24.8.0", + "typescript": "^5.2.2", + }, + }, + "packages/uploader": { + "name": "@medialit/uploader", + "version": "0.1.0", + "dependencies": { + "tus-js-client": "^4.3.1", + }, + "devDependencies": { + "@types/node": "^20.0.0", + "tsup": "^8.0.0", + "tsx": "^4.19.3", + "typescript": "^5.0.0", + }, + }, + "packages/utils": { + "name": "@medialit/utils", + "version": "0.1.0", + "dependencies": { + "nanoid": "^3.3.18", + }, + "devDependencies": { + "@types/node": "^20.9.4", + "typescript": "^5.2.2", + }, + }, + }, + "overrides": { + "@babel/core@>=7.0.0 <8.0.0": { + ".": "7.29.6", + }, + "@eslint/plugin-kit@>=0.2.0 <0.3.0": { + ".": "0.3.5", + }, + "@hono/node-server@>=1.0.0 <2.0.0": { + ".": "1.19.15", + }, + "@humanfs/node@>=0.16.0 <0.17.0": { + ".": "0.16.8", + }, + "ajv@>=6.0.0 <7.0.0": { + ".": "6.14.0", + }, + "baseline-browser-mapping@>=2.0.0 <3.0.0": { + ".": "2.11.0", + }, + "body-parser@>=1.0.0 <2.0.0": { + ".": "1.20.6", + }, + "body-parser@>=2.0.0 <3.0.0": { + ".": "2.3.0", + }, + "brace-expansion@>=1.0.0 <2.0.0": { + ".": "1.1.21", + }, + "brace-expansion@>=2.0.0 <3.0.0": { + ".": "2.1.7", + }, + "browserslist@>=4.0.0 <5.0.0": { + ".": "4.28.7", + }, + "diff@>=4.0.0 <5.0.0": { + ".": "4.0.4", + }, + "fast-uri@>=3.0.0 <4.0.0": { + ".": "3.1.8", + }, + "fast-xml-builder@>=1.0.0 <2.0.0": { + ".": "1.1.7", + }, + "fast-xml-parser@>=5.0.0 <6.0.0": { + ".": "5.7.0", + }, + "flatted@>=3.0.0 <4.0.0": { + ".": "3.4.2", + }, + "form-data@>=2.0.0 <3.0.0": { + ".": "2.5.6", + }, + "form-data@>=4.0.0 <5.0.0": { + ".": "4.0.6", + }, + "glob@>=10.0.0 <11.0.0": { + ".": "10.5.0", + }, + "hono@>=4.0.0 <5.0.0": { + ".": "4.13.7", + }, + "image-size@>=2.0.0 <3.0.0": { + ".": "2.0.3", + }, + "ip-address@>=10.0.0 <11.0.0": { + ".": "10.7.1", + }, + "joi@>=17.0.0 <18.0.0": { + ".": "17.13.7", + }, + "js-yaml@>=3.0.0 <4.0.0": { + ".": "3.15.2", + }, + "js-yaml@>=4.0.0 <5.0.0": { + ".": "4.3.2", + }, + "jws@>=3.0.0 <4.0.0": { + ".": "3.2.3", + }, + "lodash@>=4.0.0 <5.0.0": { + ".": "4.18.0", + }, + "mdast-util-to-hast@>=13.0.0 <14.0.0": { + ".": "13.2.1", + }, + "minimatch@>=3.0.0 <4.0.0": { + ".": "3.1.4", + }, + "minimatch@>=9.0.0 <10.0.0": { + ".": "9.0.7", + }, + "nanoid@>=3.0.0 <4.0.0": { + ".": "3.3.18", + }, + "next@>=16.0.0 <17.0.0": { + ".": "16.3.6", + }, + "path-to-regexp@>=0.1.0 <0.2.0": { + ".": "0.1.13", + }, + "path-to-regexp@>=8.0.0 <9.0.0": { + ".": "8.4.0", + }, + "picomatch@>=2.0.0 <3.0.0": { + ".": "2.3.2", + }, + "picomatch@>=4.0.0 <5.0.0": { + ".": "4.0.4", + }, + "postcss@>=8.0.0 <9.0.0": { + ".": "8.5.23", + }, + "postcss-selector-parser@>=6.0.0 <7.0.0": { + ".": "6.1.3", + }, + "postcss-selector-parser@>=7.0.0 <8.0.0": { + ".": "7.1.3", + }, + "qs@>=6.0.0 <7.0.0": { + ".": "6.16.0", + }, + "rollup@>=4.0.0 <5.0.0": { + ".": "4.59.0", + }, + "tmp@>=0.0.0 <1.0.0": { + ".": "0.2.7", + }, + "ws@>=5.0.0 <6.0.0": { + ".": "5.2.5", + }, + "yaml@>=2.0.0 <3.0.0": { + ".": "2.8.3", + }, + }, + "packages": { + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], + + "@aws-sdk/checksums": ["@aws-sdk/checksums@3.1001.1", "", { "dependencies": { "@aws-sdk/core": "^3.978.1", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-x12Q17KYlJAd3nKf8LV5LV0vt8sh8/6YfQLGPtrGnQf/tW4jqxPGq5GPpuVitpQYM3eUR4XB7CbxZf751NMbLw=="], + + "@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.1146.0", "", { "dependencies": { "@aws-sdk/checksums": "^3.1001.1", "@aws-sdk/core": "^3.978.1", "@aws-sdk/credential-provider-node": "^3.972.84", "@aws-sdk/middleware-sdk-s3": "^3.972.77", "@aws-sdk/signature-v4-multi-region": "^3.996.47", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.0", "@smithy/fetch-http-handler": "^5.8.0", "@smithy/node-http-handler": "^4.12.1", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-WY0YCBzxc4muFfY6UbGpA+oib0nGT/Px2aEPGYX7pp1la2OmDLChnK81mu8H7LdYvwGU1pLAsLIa/qgad0Wd7g=="], + + "@aws-sdk/cloudfront-signer": ["@aws-sdk/cloudfront-signer@3.1138.0", "", { "dependencies": { "@smithy/core": "^3.35.0", "tslib": "^2.6.2" } }, "sha512-LEyxdltaei3XslmaRt6rtqDGXs8xCCB+Cx1PaqQYJOE2fGf53z9CoHnWxZxz0ABksXMKAQ6vnUVTMEO6PU/FtA=="], + + "@aws-sdk/core": ["@aws-sdk/core@3.978.1", "", { "dependencies": { "@aws-sdk/types": "^3.974.6", "@aws-sdk/xml-builder": "^3.972.41", "@aws/lambda-invoke-store": "^0.3.0", "@smithy/core": "^3.35.0", "@smithy/signature-v4": "^5.7.3", "@smithy/types": "^4.19.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-LbY9aGsEiznDWmUc30Nwv3aIX/+dbwTx8KfS0yOC3NPYMO+O91e6jkT1azf34FwjOndq8/Q+RcVVZz5xnerwdg=="], + + "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.72", "", { "dependencies": { "@aws-sdk/core": "^3.978.1", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-xTKO/FWJPozTIXbozVnVGoNBhaGba8TBcx+KyUjRVeOlXE+dUc7GTR1cLvu0uTdIdmemzaFbqqCshXeZA1fZew=="], + + "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.74", "", { "dependencies": { "@aws-sdk/core": "^3.978.1", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.0", "@smithy/fetch-http-handler": "^5.8.0", "@smithy/node-http-handler": "^4.12.1", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-u91E/hT8f4d1xy0Jl7VG4nVKJ3lxbrZkoBTeSVoJdWBiSEUMwMS/9+e0H/aJVQV//Lt5wuzP+E69v4aRSsNTmw=="], + + "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.973.17", "", { "dependencies": { "@aws-sdk/core": "^3.978.1", "@aws-sdk/credential-provider-env": "^3.972.72", "@aws-sdk/credential-provider-http": "^3.972.74", "@aws-sdk/credential-provider-login": "^3.972.79", "@aws-sdk/credential-provider-process": "^3.972.72", "@aws-sdk/credential-provider-sso": "^3.973.16", "@aws-sdk/credential-provider-web-identity": "^3.972.78", "@aws-sdk/nested-clients": "^3.997.46", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.0", "@smithy/credential-provider-imds": "^4.5.2", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-ged4KXdBkvIC81bLvNHHuQKdKak/VXhQTR1NWYTTqW0474nlmsxy9O/vlgTIohDDWH3xpBdtVMZRyjb+DnocDA=="], + + "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.79", "", { "dependencies": { "@aws-sdk/core": "^3.978.1", "@aws-sdk/nested-clients": "^3.997.46", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-L+Z85anONJd8MaiuraO4wRxATCdEejBZ3K3eymzWI5JPXa9sOS9CkIm72PBKqXKX+Z9p9NGMX5AIMXm0LEflgw=="], + + "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.84", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.72", "@aws-sdk/credential-provider-http": "^3.972.74", "@aws-sdk/credential-provider-ini": "^3.973.17", "@aws-sdk/credential-provider-process": "^3.972.72", "@aws-sdk/credential-provider-sso": "^3.973.16", "@aws-sdk/credential-provider-web-identity": "^3.972.78", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.0", "@smithy/credential-provider-imds": "^4.5.2", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-oHt854odINVwzwsh+c5x69j0ajm4DbqqqVJ+O1ECsCIZeMDAbzFpXItaqP7UZstJj/ATdTk/KFSH0LaNAgV+kA=="], + + "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.72", "", { "dependencies": { "@aws-sdk/core": "^3.978.1", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-rLIp2xbMjX/k9/od7APpqq1ZgXXnV0pOL1Th3ZsL8Wu0TRtBsDTVS8iPqcfRFcHakFxPvR04OSTv2ka2qOb/2A=="], + + "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.973.16", "", { "dependencies": { "@aws-sdk/core": "^3.978.1", "@aws-sdk/nested-clients": "^3.997.46", "@aws-sdk/token-providers": "3.1138.0", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-IGihaJfFZYacJJr/odqILCoK7W/mvrZ7cuK7ECn3sAu4vLC6u0V8bS7mCGbdugJ8Aum2tnvqmx0F2MRFp2rn9g=="], + + "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.78", "", { "dependencies": { "@aws-sdk/core": "^3.978.1", "@aws-sdk/nested-clients": "^3.997.46", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-/y9WvNtlcPBGLR0qc1a+9J/xtYZfVczvLUOuXaVWylzttH7ewsxwHtjmiJSolNrVSDorIxHGHMU61CbonRkmwA=="], + + "@aws-sdk/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.972.77", "", { "dependencies": { "@aws-sdk/core": "^3.978.1", "@aws-sdk/signature-v4-multi-region": "^3.996.47", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-E7W2UOeUoc+lg3uIfR/dM7ZwusHwhBQrKMnlkRv4EXRR+C0YtV1pg25xC7GdZIhXH+NAMgZPCbE7o5to2cjFiw=="], + + "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.46", "", { "dependencies": { "@aws-sdk/core": "^3.978.1", "@aws-sdk/signature-v4-multi-region": "^3.996.47", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.0", "@smithy/fetch-http-handler": "^5.8.0", "@smithy/node-http-handler": "^4.12.1", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-oRxtBcka/JGHGs9l9p9IVajGoTP8vTPmoAzdHGy4Qcy9P5vPnDf6nhIeM/COQNY9k/OahImTRaLkHftoXvfcmQ=="], + + "@aws-sdk/s3-request-presigner": ["@aws-sdk/s3-request-presigner@3.1146.0", "", { "dependencies": { "@aws-sdk/core": "^3.978.1", "@aws-sdk/signature-v4-multi-region": "^3.996.47", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-5BEfVr2OYX2bwB45NSWAXPoTN5M2NPLOHohgKV+XfMNcL5TF7UQN4hAOVF4S6DwrA4BrI3MsAry7vqdZ2aJEjg=="], + + "@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.47", "", { "dependencies": { "@aws-sdk/types": "^3.974.6", "@smithy/signature-v4": "^5.7.3", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-Zk08macMvQTHzQJCLJVkOlviVoqwYMrpXv4lmLN7b7sAbiMoOK7Go0NYdR5UeF+MW8LIbRmwrNy9u/5VvX1U5g=="], + + "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1138.0", "", { "dependencies": { "@aws-sdk/core": "^3.978.1", "@aws-sdk/nested-clients": "^3.997.46", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-GpyAr0DD63YOEmYFM6Df+gJuIgC92MMTiBK4FTKfxii5MJ9ge20epR7LyroulscYlG89J+ZB2ivFDPjvfQhzdw=="], + + "@aws-sdk/types": ["@aws-sdk/types@3.974.6", "", { "dependencies": { "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-v/clNZzZnDxGyvpHMOGpJKVXFAExJzUNAAjaWGdcx8QAcXLGwTaOkw33p5SHAi0YAioK32xB3hWwOekRVfmfKg=="], + + "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.41", "", { "dependencies": { "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-ctjVSyCMegrWfXlx6VqzSBFI6UqmQ5ZlnfMhdLIiWmhoH8UAQxSCP5N3OpG7X3k4LnS7ou74C4mt20+bfTW2aQ=="], + + "@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.3.0", "", {}, "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ=="], + + "@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], + + "@babel/compat-data": ["@babel/compat-data@7.29.7", "", {}, "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg=="], + + "@babel/core": ["@babel/core@7.29.6", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.6", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.29.2", "@babel/parser": "^7.29.3", "@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.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-QdxmAo/ikZqqRGA8s43ww8lcql6naWRvEz0FFrl6MIlc7Gi6TroXnSdWa5U/kq6fzcpqpHesicQxFZIieZbyIA=="], + + "@babel/generator": ["@babel/generator@7.29.8", "", { "dependencies": { "@babel/parser": "^7.29.8", "@babel/types": "^7.29.8", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg=="], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.29.7", "", { "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g=="], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.29.7", "", { "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg=="], + + "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.26.5", "", {}, "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.29.7", "", {}, "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw=="], + + "@babel/helpers": ["@babel/helpers@7.29.7", "", { "dependencies": { "@babel/template": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg=="], + + "@babel/parser": ["@babel/parser@7.29.9", "", { "dependencies": { "@babel/types": "^7.29.8" }, "bin": "./bin/babel-parser.js" }, "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA=="], + + "@babel/plugin-syntax-object-rest-spread": ["@babel/plugin-syntax-object-rest-spread@7.8.3", "", { "dependencies": { "@babel/helper-plugin-utils": "7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA=="], + + "@babel/runtime": ["@babel/runtime@7.27.0", "", { "dependencies": { "regenerator-runtime": "0.14.1" } }, "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw=="], + + "@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], + + "@babel/traverse": ["@babel/traverse@7.29.8", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.8", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.8", "@babel/template": "^7.29.7", "@babel/types": "^7.29.8", "debug": "^4.3.1" } }, "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg=="], + + "@babel/types": ["@babel/types@7.29.8", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg=="], + + "@better-auth/cimd": ["@better-auth/cimd@1.7.7", "", { "peerDependencies": { "@better-auth/core": "^1.7.7", "@better-auth/oauth-provider": "^1.7.7", "better-auth": "^1.7.7", "better-call": "1.4.0" } }, "sha512-jFSuQ3B77usVZbPxqDdsDgd78Gy8ZwwLxAyfDJwZrvmSyDMB0Qt9rHba6chBXjvXsyiaItmdWeF/hlhbl6S1Zg=="], + + "@better-auth/core": ["@better-auth/core@1.7.7", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.41.1", "@standard-schema/spec": "^1.1.0", "zod": "^4.5.4" }, "peerDependencies": { "@better-auth/utils": "0.4.2", "@better-fetch/fetch": "1.3.2", "@opentelemetry/api": "^1.9.0", "better-call": "1.4.0", "jose": "^6.1.0", "kysely": "^0.28.5 || ^0.29.0", "nanostores": "^1.0.1" }, "optionalPeers": ["@opentelemetry/api"] }, "sha512-srgFzHEjB2WWlokFz1i4BfG41sNhsA105Pukgqu6RrEh5A+LSkzf1mCHydVipkjt9l5qswtBMi7IL6ouHbZL4Q=="], + + "@better-auth/drizzle-adapter": ["@better-auth/drizzle-adapter@1.7.7", "", { "peerDependencies": { "@better-auth/core": "^1.7.7", "@better-auth/utils": "0.4.2", "drizzle-orm": "^0.45.2 || >=1.0.0-rc.1 <2.0.0" }, "optionalPeers": ["drizzle-orm"] }, "sha512-qon0U94PR5FQysuyGoAlGVQpIIiNg5dtDpwjE63sN/HjoRWZadZzHPcS9mn7JsJyQZK6iTNhd7LD0AaXt2f78w=="], + + "@better-auth/kysely-adapter": ["@better-auth/kysely-adapter@1.7.7", "", { "peerDependencies": { "@better-auth/core": "^1.7.7", "@better-auth/utils": "0.4.2", "kysely": "^0.28.17 || ^0.29.0" }, "optionalPeers": ["kysely"] }, "sha512-9FONOCgOcrQI9wJ5v1oDGIg9sT7pa9RjZZQ4fsCTudo8R87SrIGNUvUxDFTJhZvUblvFfGaIBR9Vb8LA+8bqLQ=="], + + "@better-auth/memory-adapter": ["@better-auth/memory-adapter@1.7.7", "", { "peerDependencies": { "@better-auth/core": "^1.7.7", "@better-auth/utils": "0.4.2" } }, "sha512-FqKFEe+b5tXXV0gRbyirca+qXgMpLOeJ8Wk19yykb/scQKy0WJ4k22WzEV3TTPctQV9DZhwCXPlDtQhnlOv5Gg=="], + + "@better-auth/mongo-adapter": ["@better-auth/mongo-adapter@1.7.7", "", { "peerDependencies": { "@better-auth/core": "^1.7.7", "@better-auth/utils": "0.4.2", "mongodb": "^6.0.0 || ^7.0.0" }, "optionalPeers": ["mongodb"] }, "sha512-ZnVDuRrXqbf0oHphrEN27oTImNH5NTO26dtWatq5cXyYAsH7goof9QIoTOmxTbBxUQahKBw9T6+9h6n+RtOE+g=="], + + "@better-auth/oauth-provider": ["@better-auth/oauth-provider@1.7.7", "", { "dependencies": { "jose": "^6.2.3", "zod": "^4.5.4" }, "peerDependencies": { "@better-auth/core": "^1.7.7", "@better-auth/utils": "0.4.2", "@better-fetch/fetch": "1.3.2", "better-auth": "^1.7.7", "better-call": "1.4.0" } }, "sha512-dc4C/ZF+8qc88ebcU8kaFp8yladCtfTkuUt2zBSmYw25YCNC5BoUP9rpAkIx4nemlZ11gLtSnkEPc0sA7MwVmw=="], + + "@better-auth/prisma-adapter": ["@better-auth/prisma-adapter@1.7.7", "", { "peerDependencies": { "@better-auth/core": "^1.7.7", "@better-auth/utils": "0.4.2", "@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0", "prisma": "^5.0.0 || ^6.0.0 || ^7.0.0" }, "optionalPeers": ["@prisma/client", "prisma"] }, "sha512-4YcnrcVdvWiAZw0sZl63tWUClPXg5r3QsLn7C2sG7kKEBY06zOng0ibecyxRBixKq1zE1ceHuEUqez5Rhi+nQA=="], + + "@better-auth/telemetry": ["@better-auth/telemetry@1.7.7", "", { "peerDependencies": { "@better-auth/core": "^1.7.7", "@better-auth/utils": "0.4.2", "@better-fetch/fetch": "1.3.2" } }, "sha512-PaRA6i+kAioVYmza2gHPcdrxP0hAotph4KV9hft2GeNJq+AZXDiBu3FuLM9HxvcmiZeUT74Vufqd9ypmB8veTA=="], + + "@better-auth/utils": ["@better-auth/utils@0.4.2", "", { "dependencies": { "@noble/hashes": "^2.0.1" } }, "sha512-AUxrvu+HaaODsUyzDxFgwd/8RZ1yZaYo42LXKSrU2oGgR38pS1ij8nqQKNgtTWoYGpNevNXtCfgTy6loHveW9A=="], + + "@better-fetch/fetch": ["@better-fetch/fetch@1.3.2", "", {}, "sha512-Gs7n99b5tqUC6cQAPbV0uED3IraHB6xQbHLQ/C3l7ZFafHScOx9pQ+DYmP5blbLFShVWLqxNUlI9wi4xU/X+ow=="], + + "@changesets/apply-release-plan": ["@changesets/apply-release-plan@7.0.10", "", { "dependencies": { "@changesets/config": "3.1.1", "@changesets/get-version-range-type": "0.4.0", "@changesets/git": "3.0.2", "@changesets/should-skip-package": "0.1.2", "@changesets/types": "6.1.0", "@manypkg/get-packages": "1.1.3", "detect-indent": "6.1.0", "fs-extra": "7.0.1", "lodash.startcase": "4.4.0", "outdent": "0.5.0", "prettier": "2.8.8", "resolve-from": "5.0.0", "semver": "7.7.1" } }, "sha512-wNyeIJ3yDsVspYvHnEz1xQDq18D9ifed3lI+wxRQRK4pArUcuHgCTrHv0QRnnwjhVCQACxZ+CBih3wgOct6UXw=="], + + "@changesets/assemble-release-plan": ["@changesets/assemble-release-plan@6.0.6", "", { "dependencies": { "@changesets/errors": "0.2.0", "@changesets/get-dependents-graph": "2.1.3", "@changesets/should-skip-package": "0.1.2", "@changesets/types": "6.1.0", "@manypkg/get-packages": "1.1.3", "semver": "7.7.1" } }, "sha512-Frkj8hWJ1FRZiY3kzVCKzS0N5mMwWKwmv9vpam7vt8rZjLL1JMthdh6pSDVSPumHPshTTkKZ0VtNbE0cJHZZUg=="], + + "@changesets/changelog-git": ["@changesets/changelog-git@0.2.1", "", { "dependencies": { "@changesets/types": "6.1.0" } }, "sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q=="], + + "@changesets/cli": ["@changesets/cli@2.28.1", "", { "dependencies": { "@changesets/apply-release-plan": "7.0.10", "@changesets/assemble-release-plan": "6.0.6", "@changesets/changelog-git": "0.2.1", "@changesets/config": "3.1.1", "@changesets/errors": "0.2.0", "@changesets/get-dependents-graph": "2.1.3", "@changesets/get-release-plan": "4.0.8", "@changesets/git": "3.0.2", "@changesets/logger": "0.1.1", "@changesets/pre": "2.0.2", "@changesets/read": "0.6.3", "@changesets/should-skip-package": "0.1.2", "@changesets/types": "6.1.0", "@changesets/write": "0.4.0", "@manypkg/get-packages": "1.1.3", "ansi-colors": "4.1.3", "ci-info": "3.9.0", "enquirer": "2.4.1", "external-editor": "3.1.0", "fs-extra": "7.0.1", "mri": "1.2.0", "p-limit": "2.3.0", "package-manager-detector": "0.2.11", "picocolors": "1.1.1", "resolve-from": "5.0.0", "semver": "7.7.1", "spawndamnit": "3.0.1", "term-size": "2.2.1" }, "bin": { "changeset": "bin.js" } }, "sha512-PiIyGRmSc6JddQJe/W1hRPjiN4VrMvb2VfQ6Uydy2punBioQrsxppyG5WafinKcW1mT0jOe/wU4k9Zy5ff21AA=="], + + "@changesets/config": ["@changesets/config@3.1.1", "", { "dependencies": { "@changesets/errors": "0.2.0", "@changesets/get-dependents-graph": "2.1.3", "@changesets/logger": "0.1.1", "@changesets/types": "6.1.0", "@manypkg/get-packages": "1.1.3", "fs-extra": "7.0.1", "micromatch": "4.0.8" } }, "sha512-bd+3Ap2TKXxljCggI0mKPfzCQKeV/TU4yO2h2C6vAihIo8tzseAn2e7klSuiyYYXvgu53zMN1OeYMIQkaQoWnA=="], + + "@changesets/errors": ["@changesets/errors@0.2.0", "", { "dependencies": { "extendable-error": "0.1.7" } }, "sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow=="], + + "@changesets/get-dependents-graph": ["@changesets/get-dependents-graph@2.1.3", "", { "dependencies": { "@changesets/types": "6.1.0", "@manypkg/get-packages": "1.1.3", "picocolors": "1.1.1", "semver": "7.7.1" } }, "sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ=="], + + "@changesets/get-release-plan": ["@changesets/get-release-plan@4.0.8", "", { "dependencies": { "@changesets/assemble-release-plan": "6.0.6", "@changesets/config": "3.1.1", "@changesets/pre": "2.0.2", "@changesets/read": "0.6.3", "@changesets/types": "6.1.0", "@manypkg/get-packages": "1.1.3" } }, "sha512-MM4mq2+DQU1ZT7nqxnpveDMTkMBLnwNX44cX7NSxlXmr7f8hO6/S2MXNiXG54uf/0nYnefv0cfy4Czf/ZL/EKQ=="], + + "@changesets/get-version-range-type": ["@changesets/get-version-range-type@0.4.0", "", {}, "sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ=="], + + "@changesets/git": ["@changesets/git@3.0.2", "", { "dependencies": { "@changesets/errors": "0.2.0", "@manypkg/get-packages": "1.1.3", "is-subdir": "1.2.0", "micromatch": "4.0.8", "spawndamnit": "3.0.1" } }, "sha512-r1/Kju9Y8OxRRdvna+nxpQIsMsRQn9dhhAZt94FLDeu0Hij2hnOozW8iqnHBgvu+KdnJppCveQwK4odwfw/aWQ=="], + + "@changesets/logger": ["@changesets/logger@0.1.1", "", { "dependencies": { "picocolors": "1.1.1" } }, "sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg=="], + + "@changesets/parse": ["@changesets/parse@0.4.1", "", { "dependencies": { "@changesets/types": "6.1.0", "js-yaml": "3.14.1" } }, "sha512-iwksMs5Bf/wUItfcg+OXrEpravm5rEd9Bf4oyIPL4kVTmJQ7PNDSd6MDYkpSJR1pn7tz/k8Zf2DhTCqX08Ou+Q=="], + + "@changesets/pre": ["@changesets/pre@2.0.2", "", { "dependencies": { "@changesets/errors": "0.2.0", "@changesets/types": "6.1.0", "@manypkg/get-packages": "1.1.3", "fs-extra": "7.0.1" } }, "sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug=="], + + "@changesets/read": ["@changesets/read@0.6.3", "", { "dependencies": { "@changesets/git": "3.0.2", "@changesets/logger": "0.1.1", "@changesets/parse": "0.4.1", "@changesets/types": "6.1.0", "fs-extra": "7.0.1", "p-filter": "2.1.0", "picocolors": "1.1.1" } }, "sha512-9H4p/OuJ3jXEUTjaVGdQEhBdqoT2cO5Ts95JTFsQyawmKzpL8FnIeJSyhTDPW1MBRDnwZlHFEM9SpPwJDY5wIg=="], + + "@changesets/should-skip-package": ["@changesets/should-skip-package@0.1.2", "", { "dependencies": { "@changesets/types": "6.1.0", "@manypkg/get-packages": "1.1.3" } }, "sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw=="], + + "@changesets/types": ["@changesets/types@6.1.0", "", {}, "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA=="], + + "@changesets/write": ["@changesets/write@0.4.0", "", { "dependencies": { "@changesets/types": "6.1.0", "fs-extra": "7.0.1", "human-id": "4.1.1", "prettier": "2.8.8" } }, "sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q=="], + + "@cnakazawa/watch": ["@cnakazawa/watch@1.0.4", "", { "dependencies": { "exec-sh": "0.3.6", "minimist": "1.2.8" }, "bin": { "watch": "cli.js" } }, "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ=="], + + "@codelitdev/billing": ["@codelitdev/billing@0.3.0", "", { "dependencies": { "drizzle-orm": "^0.45.2", "nanoid": "^5.1.6", "zod": "^3.25.76" }, "peerDependencies": { "dodopayments": "^2.48.0" }, "optionalPeers": ["dodopayments"], "bin": { "codelit-billing": "bin/codelit-billing.js" } }, "sha512-mfGZ9tac8d8H7X5BQA2WCIjtxX/NNlKNaIb19PmJyxFaxAuN6xYt6mOwU7gY/nSHQSetCw3JtpTXGu0Bc7P9kw=="], + + "@codelitdev/design-system": ["@codelitdev/design-system@0.3.0", "", { "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-z9+0VSd9oB89Dgi9lfpX211TVa7mE8vrEpDp5++4Z3RMvk4E51iEOLLemo+wrYACROXhH++jYxYnKZGE3EHlpg=="], + + "@codelitdev/mcp-server-kit": ["@codelitdev/mcp-server-kit@0.3.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "^1.30.0", "zod": "^3.25.76" }, "peerDependencies": { "@codelitdev/platform": ">=0.3.0" } }, "sha512-2K0b1zoeu7n2PlHajw3bGgWkC+ea1oJLBK9EYFvD9pubWQxaytJZzSq1ZGtYgOsUweVlnP63IbzweoQYTz7aqA=="], + + "@codelitdev/oauth-server-kit": ["@codelitdev/oauth-server-kit@0.3.0", "", { "peerDependencies": { "@better-auth/oauth-provider": "^1.7.2", "better-auth": "^1.7.2", "express": "^4.21.2" } }, "sha512-jztOOAGb176MhtFBAD5wRN+id1nbkDRVhwXIVq4ij8P4mB31ej6hZLSxO2XmwyUtxdsHFr7uzgpO+C4ZhlHRHA=="], + + "@codelitdev/observability": ["@codelitdev/observability@0.3.0", "", { "dependencies": { "pino": "^9.6.0" }, "peerDependencies": { "express": "^4.21.2", "posthog-js": "^1.200.0", "posthog-node": "^4.0.0" }, "optionalPeers": ["express", "posthog-js", "posthog-node"] }, "sha512-QWEsI1667kCu01FLcx+70BS/fmxx1qeNX68lnmCiz5KJfKOg8OtuDVPolZq6fyqsrzqN1Sd2t1AYHkFGsTIQfA=="], + + "@codelitdev/platform": ["@codelitdev/platform@0.3.0", "", {}, "sha512-Yp3nCYlqEMbHv2usZFWCSrMtGp5/zI/ZT/bGT0LYEnvxgk0YJReYktiMpjfTKajEuWKl+WmiZvT8iZMcDy6c3A=="], + + "@codelitdev/platform-conformance": ["@codelitdev/platform-conformance@0.3.0", "", { "peerDependencies": { "@codelitdev/platform": ">=0.3.0" } }, "sha512-N7JsCQF4TRQ4A+Y3nu9bkXj6WEXZN2IrSIEU9glgQH0N+KBgGi/rlP2rtrnSK61jrbf1tbZn8yE7bZWTO08b2g=="], + + "@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="], + + "@electric-sql/pglite": ["@electric-sql/pglite@0.5.8", "", {}, "sha512-n9tsbUOhwx2epK1V0ZG9Ar4SHWUju04dhmzZXiSBXwBoleOvIfals33NAaWgagQVAL4Rbvx/Ptsu3P+pA09f6Q=="], + + "@emnapi/core": ["@emnapi/core@1.4.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.1", "tslib": "2.8.1" } }, "sha512-H+N/FqT07NmLmt6OFFtDfwe8PNygprzBikrEMyQfgqSmT0vzE515Pz7R8izwB9q/zsH/MA64AKoul3sA6/CzVg=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], + + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.1", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw=="], + + "@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="], + + "@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.10.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], + + "@eslint/config-array": ["@eslint/config-array@0.21.2", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.5" } }, "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw=="], + + "@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="], + + "@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], + + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.7", "", { "dependencies": { "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.3.2", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" } }, "sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw=="], + + "@eslint/js": ["@eslint/js@9.24.0", "", {}, "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA=="], + + "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], + + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], + + "@example/express": ["@example/express@workspace:examples/express"], + + "@example/next-app-router": ["@example/next-app-router@workspace:examples/next-app-router"], + + "@fastify/deepmerge": ["@fastify/deepmerge@3.2.1", "", {}, "sha512-N5Oqvltoa2r9z1tbx4xjky0oRR60v+T47Ic4J1ukoVQcptLOrIdRnCSdTGmOmajZuHVKlTnfcmrjyqsGEW1ztA=="], + + "@floating-ui/core": ["@floating-ui/core@1.6.9", "", { "dependencies": { "@floating-ui/utils": "0.2.9" } }, "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.6.13", "", { "dependencies": { "@floating-ui/core": "1.6.9", "@floating-ui/utils": "0.2.9" } }, "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w=="], + + "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.2", "", { "dependencies": { "@floating-ui/dom": "1.6.13" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.9", "", {}, "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg=="], + + "@formatjs/fast-memoize": ["@formatjs/fast-memoize@3.1.1", "", {}, "sha512-CbNbf+tlJn1baRnPkNePnBqTLxGliG6DDgNa/UtV66abwIjwsliPMOt0172tzxABYzSuxZBZfcp//qI8AvBWPg=="], + + "@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.8.2", "", { "dependencies": { "@formatjs/fast-memoize": "3.1.1" } }, "sha512-q05KMYGJLyqFNFtIb8NhWLF5X3aK/k0wYt7dnRFuy6aLQL+vUwQ1cg5cO4qawEiINybeCPXAWlprY2mSBjSXAQ=="], + + "@fumadocs/tailwind": ["@fumadocs/tailwind@0.0.3", "", { "dependencies": { "postcss-selector-parser": "7.1.1" }, "peerDependencies": { "tailwindcss": "^4.0.0" }, "optionalPeers": ["tailwindcss"] }, "sha512-/FWcggMz9BhoX+13xBoZLX+XX9mYvJ50dkTqy3IfocJqua65ExcsKfxwKH8hgTO3vA5KnWv4+4jU7LaW2AjAmQ=="], + + "@fumari/json-schema-ts": ["@fumari/json-schema-ts@0.0.2", "", { "dependencies": { "esrap": "2.2.4" }, "peerDependencies": { "json-schema-typed": "^8.0.2" }, "optionalPeers": ["json-schema-typed"] }, "sha512-A2x8nj45r8Kc3Gqa+HpWRF9uzIMc9dySB6L2R2kiyjLHXWBsZUX99Atj5+Yup/iRQXQ9s8AX+uAPwPze7Xn05A=="], + + "@fumari/stf": ["@fumari/stf@1.0.3", "", { "peerDependencies": { "@types/react": "*", "react": "^19.2.0", "react-dom": "^19.2.0" }, "optionalPeers": ["@types/react"] }, "sha512-rSHaCnCREbDFTD4BNU40ZNjvkhg5wcKzJ1OPsPeKbFzVo9gmm1JE2lGhqsfPhDhuBOV+ZNqZy9+5T+JudKY1NA=="], + + "@hapi/hoek": ["@hapi/hoek@9.3.0", "", {}, "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ=="], + + "@hapi/topo": ["@hapi/topo@5.1.0", "", { "dependencies": { "@hapi/hoek": "9.3.0" } }, "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg=="], + + "@hono/node-server": ["@hono/node-server@1.19.15", "", { "peerDependencies": { "hono": "^4" } }, "sha512-Za2ai6TLdKjUvnur+eenO6nuYYipVAEhyCAdaV8IRvmU9kK8crOZUSYvIXn72E4f8fJqyAbpcJuTsYYmZp9Deg=="], + + "@humanfs/core": ["@humanfs/core@0.19.2", "", { "dependencies": { "@humanfs/types": "^0.15.0" } }, "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA=="], + + "@humanfs/node": ["@humanfs/node@0.16.8", "", { "dependencies": { "@humanfs/core": "^0.19.2", "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ=="], + + "@humanfs/types": ["@humanfs/types@0.15.0", "", {}, "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q=="], + + "@humanwhocodes/config-array": ["@humanwhocodes/config-array@0.13.0", "", { "dependencies": { "@humanwhocodes/object-schema": "2.0.3", "debug": "4.4.3", "minimatch": "3.1.2" } }, "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/object-schema": ["@humanwhocodes/object-schema@2.0.3", "", {}, "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA=="], + + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.2", "", {}, "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ=="], + + "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], + + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg=="], + + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.4" }, "os": "darwin", "cpu": "x64" }, "sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw=="], + + "@img/sharp-freebsd-wasm32": ["@img/sharp-freebsd-wasm32@0.35.5", "", { "dependencies": { "@img/sharp-wasm32": "0.35.5" }, "os": "freebsd" }, "sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw=="], + + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw=="], + + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ=="], + + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.4", "", { "os": "linux", "cpu": "arm" }, "sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw=="], + + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A=="], + + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.3.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ=="], + + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.3.4", "", { "os": "linux", "cpu": "none" }, "sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q=="], + + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ=="], + + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw=="], + + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA=="], + + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg=="], + + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.4" }, "os": "linux", "cpu": "arm" }, "sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ=="], + + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.4" }, "os": "linux", "cpu": "arm64" }, "sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ=="], + + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.3.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA=="], + + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.3.4" }, "os": "linux", "cpu": "none" }, "sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ=="], + + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.4" }, "os": "linux", "cpu": "s390x" }, "sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA=="], + + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.4" }, "os": "linux", "cpu": "x64" }, "sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog=="], + + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.4" }, "os": "linux", "cpu": "arm64" }, "sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg=="], + + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.4" }, "os": "linux", "cpu": "x64" }, "sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A=="], + + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.35.5", "", { "dependencies": { "@emnapi/runtime": "^1.11.3" } }, "sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ=="], + + "@img/sharp-webcontainers-wasm32": ["@img/sharp-webcontainers-wasm32@0.35.5", "", { "dependencies": { "@img/sharp-wasm32": "0.35.5" }, "cpu": "none" }, "sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA=="], + + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.35.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q=="], + + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A=="], + + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.5", "", { "os": "win32", "cpu": "x64" }, "sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg=="], + + "@ioredis/commands": ["@ioredis/commands@1.4.0", "", {}, "sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ=="], + + "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "5.1.2", "string-width-cjs": "npm:string-width@4.2.3", "strip-ansi": "7.1.0", "strip-ansi-cjs": "npm:strip-ansi@6.0.1", "wrap-ansi": "8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], + + "@jest/console": ["@jest/console@24.9.0", "", { "dependencies": { "@jest/source-map": "24.9.0", "chalk": "2.4.2", "slash": "2.0.0" } }, "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ=="], + + "@jest/core": ["@jest/core@24.9.0", "", { "dependencies": { "@jest/console": "24.9.0", "@jest/reporters": "24.9.0", "@jest/test-result": "24.9.0", "@jest/transform": "24.9.0", "@jest/types": "24.9.0", "ansi-escapes": "3.2.0", "chalk": "2.4.2", "exit": "0.1.2", "graceful-fs": "4.2.11", "jest-changed-files": "24.9.0", "jest-config": "24.9.0", "jest-haste-map": "24.9.0", "jest-message-util": "24.9.0", "jest-regex-util": "24.9.0", "jest-resolve": "24.9.0", "jest-resolve-dependencies": "24.9.0", "jest-runner": "24.9.0", "jest-runtime": "24.9.0", "jest-snapshot": "24.9.0", "jest-util": "24.9.0", "jest-validate": "24.9.0", "jest-watcher": "24.9.0", "micromatch": "3.1.10", "p-each-series": "1.0.0", "realpath-native": "1.1.0", "rimraf": "2.7.1", "slash": "2.0.0", "strip-ansi": "5.2.0" } }, "sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A=="], + + "@jest/environment": ["@jest/environment@24.9.0", "", { "dependencies": { "@jest/fake-timers": "24.9.0", "@jest/transform": "24.9.0", "@jest/types": "24.9.0", "jest-mock": "24.9.0" } }, "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ=="], + + "@jest/fake-timers": ["@jest/fake-timers@24.9.0", "", { "dependencies": { "@jest/types": "24.9.0", "jest-message-util": "24.9.0", "jest-mock": "24.9.0" } }, "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A=="], + + "@jest/reporters": ["@jest/reporters@24.9.0", "", { "dependencies": { "@jest/environment": "24.9.0", "@jest/test-result": "24.9.0", "@jest/transform": "24.9.0", "@jest/types": "24.9.0", "chalk": "2.4.2", "exit": "0.1.2", "glob": "7.2.3", "istanbul-lib-coverage": "2.0.5", "istanbul-lib-instrument": "3.3.0", "istanbul-lib-report": "2.0.8", "istanbul-lib-source-maps": "3.0.6", "istanbul-reports": "2.2.7", "jest-haste-map": "24.9.0", "jest-resolve": "24.9.0", "jest-runtime": "24.9.0", "jest-util": "24.9.0", "jest-worker": "24.9.0", "node-notifier": "5.4.5", "slash": "2.0.0", "source-map": "0.6.1", "string-length": "2.0.0" } }, "sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw=="], + + "@jest/source-map": ["@jest/source-map@24.9.0", "", { "dependencies": { "callsites": "3.1.0", "graceful-fs": "4.2.11", "source-map": "0.6.1" } }, "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg=="], + + "@jest/test-result": ["@jest/test-result@24.9.0", "", { "dependencies": { "@jest/console": "24.9.0", "@jest/types": "24.9.0", "@types/istanbul-lib-coverage": "2.0.6" } }, "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA=="], + + "@jest/test-sequencer": ["@jest/test-sequencer@24.9.0", "", { "dependencies": { "@jest/test-result": "24.9.0", "jest-haste-map": "24.9.0", "jest-runner": "24.9.0", "jest-runtime": "24.9.0" } }, "sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A=="], + + "@jest/transform": ["@jest/transform@24.9.0", "", { "dependencies": { "@babel/core": "7.26.10", "@jest/types": "24.9.0", "babel-plugin-istanbul": "5.2.0", "chalk": "2.4.2", "convert-source-map": "1.9.0", "fast-json-stable-stringify": "2.1.0", "graceful-fs": "4.2.11", "jest-haste-map": "24.9.0", "jest-regex-util": "24.9.0", "jest-util": "24.9.0", "micromatch": "3.1.10", "pirates": "4.0.7", "realpath-native": "1.1.0", "slash": "2.0.0", "source-map": "0.6.1", "write-file-atomic": "2.4.1" } }, "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ=="], + + "@jest/types": ["@jest/types@24.9.0", "", { "dependencies": { "@types/istanbul-lib-coverage": "2.0.6", "@types/istanbul-reports": "1.1.2", "@types/yargs": "13.0.12" } }, "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "1.2.1", "@jridgewell/sourcemap-codec": "1.5.0", "@jridgewell/trace-mapping": "0.3.25" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "3.1.2", "@jridgewell/sourcemap-codec": "1.5.0" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], + + "@manypkg/find-root": ["@manypkg/find-root@1.1.0", "", { "dependencies": { "@babel/runtime": "7.27.0", "@types/node": "12.20.55", "find-up": "4.1.0", "fs-extra": "8.1.0" } }, "sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA=="], + + "@manypkg/get-packages": ["@manypkg/get-packages@1.1.3", "", { "dependencies": { "@babel/runtime": "7.27.0", "@changesets/types": "4.1.0", "@manypkg/find-root": "1.1.0", "fs-extra": "8.1.0", "globby": "11.1.0", "read-yaml-file": "1.1.0" } }, "sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A=="], + + "@mdx-js/mdx": ["@mdx-js/mdx@3.1.1", "", { "dependencies": { "@types/estree": "1.0.7", "@types/estree-jsx": "1.0.5", "@types/hast": "3.0.4", "@types/mdx": "2.0.13", "acorn": "8.14.1", "collapse-white-space": "2.1.0", "devlop": "1.1.0", "estree-util-is-identifier-name": "3.0.0", "estree-util-scope": "1.0.0", "estree-walker": "3.0.3", "hast-util-to-jsx-runtime": "2.3.6", "markdown-extensions": "2.0.0", "recma-build-jsx": "1.0.0", "recma-jsx": "1.0.0", "recma-stringify": "1.0.0", "rehype-recma": "1.0.0", "remark-mdx": "3.1.0", "remark-parse": "11.0.0", "remark-rehype": "11.1.2", "source-map": "0.7.4", "unified": "11.0.5", "unist-util-position-from-estree": "2.0.0", "unist-util-stringify-position": "4.0.0", "unist-util-visit": "5.1.0", "vfile": "6.0.3" } }, "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ=="], + + "@medialit/api": ["@medialit/api@workspace:apps/api"], + + "@medialit/cli": ["@medialit/cli@workspace:packages/cli"], + + "@medialit/docs": ["@medialit/docs@workspace:apps/docs"], + + "@medialit/images": ["@medialit/images@workspace:packages/images"], + + "@medialit/integration-tests": ["@medialit/integration-tests@workspace:packages/integration-tests"], + + "@medialit/models": ["@medialit/models@workspace:packages/models"], + + "@medialit/react": ["@medialit/react@workspace:packages/react"], + + "@medialit/scripts": ["@medialit/scripts@workspace:packages/scripts"], + + "@medialit/thumbnail": ["@medialit/thumbnail@workspace:packages/thumbnail"], + + "@medialit/uploader": ["@medialit/uploader@workspace:packages/uploader"], + + "@medialit/utils": ["@medialit/utils@workspace:packages/utils"], + + "@medialit/web": ["@medialit/web@workspace:apps/web"], + + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.30.0", "", { "dependencies": { "@hono/node-server": "^1.19.9 || ^2.0.5", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA=="], + + "@mongodb-js/saslprep": ["@mongodb-js/saslprep@1.3.2", "", { "dependencies": { "sparse-bitfield": "3.0.3" } }, "sha512-QgA5AySqB27cGTXBFmnpifAi7HxoGUeezwo6p9dI03MuDB6Pp33zgclqVb6oVK3j6I9Vesg0+oojW2XxB59SGg=="], + + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.8", "", { "dependencies": { "@emnapi/core": "1.4.0", "@emnapi/runtime": "1.7.1", "@tybys/wasm-util": "0.9.0" } }, "sha512-OBlgKdX7gin7OIq4fadsjpg+cp2ZphvAIKucHsNfTdJiqdOmOEwQd/bHi0VwNrcw5xpBJyUw6cK/QilCqy1BSg=="], + + "@next/env": ["@next/env@16.3.6", "", {}, "sha512-x9Vblze1EbtltQYnNH38xCPWU3TVfBd1eXqA3+w9+BTpedkkdNpAaltXlGQ/nsc1+E0mVTNrtcbX3GoO09zeLQ=="], + + "@next/eslint-plugin-next": ["@next/eslint-plugin-next@15.2.4", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-O8ScvKtnxkp8kL9TpJTTKnMqlkZnS+QxwoQnJwPGBxjBbzd6OVVPEJ5/pMNrktSyXQD/chEfzfFzYLM6JANOOQ=="], + + "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.3.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-E/7GEqaUkt8mk/T8v9lAnrhzR06kdq1ZBkC12F8tAMkdIadwNp3H1KqHynDHrpcTlGCUdq/qu6vUL2aYVyYBdw=="], + + "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.3.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-yBE893/nDWTlaiBD1p+qgt7NUen4U5R6FXyH0s67Npq1S3E0cVSef1WIXC2xBRgQvwAvJq6DnS6Y6PrY0cy4Ew=="], + + "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.3.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-KJDpjBqBPYlvkivmyrp+Qys6k/7ksbqGQvRVc6ZEGfR+cjQxx+nUkJaWmNZJsmoOrqYNbaXByF8wa0lBwDhB3Q=="], + + "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.3.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-mqNg2K+hvWskSRb/QM+Ix412DvBsuSF0XV+frTSw5vmoucNnIlynFwKYew8D01bfATErMOM7Bujrf0BA5DRKFA=="], + + "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.3.6", "", { "os": "linux", "cpu": "x64" }, "sha512-nFncBNGAYouRHjRVaITs9beZRfhX4ssVwpnvPIAbkZVH6LtGoAVlH4bJ8Cnf9SOo9bsXgPFer/GdHtEE3JNOkw=="], + + "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.3.6", "", { "os": "linux", "cpu": "x64" }, "sha512-5Mf3cHDGR/Iz0ng2Bj3zUR3p5QS9YK3Hn2QiAfavFmyF48zwThAjpFoiTKNIcOHLYS4zEk+gzyJ/9deQ2ZB8yQ=="], + + "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.3.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-0jkJy0C2kbrJWTk4YLa3xk80pVBpx8FCHJym7CnUfDAXe/FWv5qT7SQJbR0KuemyxaEDlEx5WT4VQJoTW+/9Qw=="], + + "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.3.6", "", { "os": "win32", "cpu": "x64" }, "sha512-/YXjI1e5OXcZ7YpxRwgP/1jAV/SBKTzeVKqN2mk7mLpcICsyn3Gl5+dIfDTJp70M0ccMhyMMRso4v6mPDCGepg=="], + + "@noble/ciphers": ["@noble/ciphers@2.4.0", "", {}, "sha512-AnjFn0Jv92laAkvMrghlFZq4qQCIN/4DxFV/eooqtC2YTjB7kBeLMS2T9KJX4Dn+ZVXLOwK0lSgqDtx9gvxtiw=="], + + "@noble/hashes": ["@noble/hashes@2.4.0", "", {}, "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA=="], + + "@nodable/entities": ["@nodable/entities@2.2.0", "", {}, "sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "1.2.0" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "1.19.1" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="], + + "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.43.0", "", {}, "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg=="], + + "@orama/orama": ["@orama/orama@3.1.18", "", {}, "sha512-a61ljmRVVyG5MC/698C8/FfFDw5a8LOIvyOLW5fztgUXqUpc1jOfQzOitSCbge657OgXXThmY3Tk8fpiDb4UcA=="], + + "@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="], + + "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], + + "@posthog/browser-common": ["@posthog/browser-common@0.9.1", "", { "dependencies": { "@posthog/core": "^1.55.3", "@posthog/types": "^1.413.0" } }, "sha512-Dl8uV2OZAxfHfWyC7K0Y3HtT7ld+WGLbXIwgCgAGziOMGDIa/bepCRrCxy7IxsEzqY1HOrq/VDdcoNN7hBTSQQ=="], + + "@posthog/core": ["@posthog/core@1.55.3", "", { "dependencies": { "@posthog/types": "^1.413.0" } }, "sha512-XnmG9jo7WlxTeN4oCNWH4ESLQHR6ZPZ+mcq+8JxU62k5i1BAe3BSrybnmXHjqKp7e3rUvLK2bOzGZGSlfHvlNA=="], + + "@posthog/types": ["@posthog/types@1.413.0", "", {}, "sha512-tYAVzNojWckuG/wxBB0fwE70sFruQQq4xbhkci7eH2ARU3mMfUTkvpN1HwBMpm0AtbxidLiH0IFvagmdJ8opPw=="], + + "@radix-ui/colors": ["@radix-ui/colors@3.0.0", "", {}, "sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg=="], + + "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], + + "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA=="], + + "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="], + + "@radix-ui/react-avatar": ["@radix-ui/react-avatar@1.1.3", "", { "dependencies": { "@radix-ui/react-context": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Paen00T4P8L8gd9bNsRMw7Cbaz85oxiv+hzomsRZgFm2byltPFDtfcoqlWJ8GyZlIBWgLssJlzLCnKU0G0302g=="], + + "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA=="], + + "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw=="], + + "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-context": ["@radix-ui/react-context@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q=="], + + "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "1.2.4", "react-remove-scroll": "2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="], + + "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], + + "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], + + "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-menu": "2.1.6", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA=="], + + "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="], + + "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], + + "@radix-ui/react-icons": ["@radix-ui/react-icons@1.3.2", "", { "peerDependencies": { "react": "^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc" } }, "sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g=="], + + "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-label": ["@radix-ui/react-label@2.1.2", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw=="], + + "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-focus-guards": "1.1.1", "@radix-ui/react-focus-scope": "1.1.2", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-popper": "1.2.2", "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-roving-focus": "1.1.2", "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-callback-ref": "1.1.0", "aria-hidden": "1.2.4", "react-remove-scroll": "2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tBBb5CXDJW3t2mo9WlO7r6GTmWV0F0uzHZVFmlRmYpiSK1CDU5IKojP1pm7oknpBOrFZx/YgBRW9oorPO2S/Lg=="], + + "@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.2.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w=="], + + "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "1.2.4", "react-remove-scroll": "2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA=="], + + "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "", { "dependencies": { "@floating-ui/react-dom": "2.1.2", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw=="], + + "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], + + "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], + + "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="], + + "@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.4", "", { "dependencies": { "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-8rl9w7lJdcVPor47Dhws9mUHRHLE+8JEgyJRdNWCpGPa6HIlr3eh+Yn9gyx1CnCLbw5naHsI2gaO9dBWO50vzw=="], + + "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], + + "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.10", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A=="], + + "@radix-ui/react-select": ["@radix-ui/react-select@2.2.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "1.2.4", "react-remove-scroll": "2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ=="], + + "@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.2", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-oZfHcaAp2Y6KFBX6I5P1u7CQoy4lheCGiYj+pGFrHy8E/VNRb5E39TkTr3JrV520csPBTZjkuKFdEsjS5EUNKQ=="], + + "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], + + "@radix-ui/react-switch": ["@radix-ui/react-switch@1.1.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-use-previous": "1.1.0", "@radix-ui/react-use-size": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1nc+vjEOQkJVsJtWPSiISGT6OKm4SiOdjMo+/icLxo2G4vxz1GntC5MzfL4v8ey9OEfw787QCD1y3mUv0NiFEQ=="], + + "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="], + + "@radix-ui/react-toast": ["@radix-ui/react-toast@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0", "@radix-ui/react-visually-hidden": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-gN4dpuIVKEgpLn1z5FhzT9mYRUitbfZq9XqN/7kkBMUgFTzTG8x/KszWJugJXHcwxckY8xcKDZPz7kG3o6DsUA=="], + + "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw=="], + + "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="], + + "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="], + + "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="], + + "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og=="], + + "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="], + + "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw=="], + + "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.1.2", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q=="], + + "@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="], + + "@redis/client": ["@redis/client@5.12.1", "", { "dependencies": { "cluster-key-slot": "1.1.2" }, "peerDependencies": { "@node-rs/xxhash": "^1.1.0", "@opentelemetry/api": ">=1 <2" }, "optionalPeers": ["@node-rs/xxhash", "@opentelemetry/api"] }, "sha512-7aPGWeqA3uFm43o19umzdl16CEjK/JQGtSXVPevplTaOU3VJA/rseBC1QvYUz9lLDIMBimc4SW/zrW4S89BaCA=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.59.0", "", { "os": "android", "cpu": "arm" }, "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.59.0", "", { "os": "android", "cpu": "arm64" }, "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.59.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.59.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.59.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.59.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.59.0", "", { "os": "linux", "cpu": "arm" }, "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.59.0", "", { "os": "linux", "cpu": "arm" }, "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.59.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.59.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA=="], + + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg=="], + + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.59.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA=="], + + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.59.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.59.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.59.0", "", { "os": "linux", "cpu": "x64" }, "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.59.0", "", { "os": "linux", "cpu": "x64" }, "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg=="], + + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.59.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ=="], + + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.59.0", "", { "os": "none", "cpu": "arm64" }, "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.59.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.59.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA=="], + + "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="], + + "@rushstack/eslint-patch": ["@rushstack/eslint-patch@1.11.0", "", {}, "sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ=="], + + "@scalar/helpers": ["@scalar/helpers@0.4.2", "", {}, "sha512-IrgrGVSahCfYDNWITazz4Q1BOndp5eEzlimRkfxiYn++KqeWyLfALyym1omqcdKGYtiSx1KIbKaUJL9vkjaN7w=="], + + "@scalar/json-magic": ["@scalar/json-magic@0.12.4", "", { "dependencies": { "@scalar/helpers": "0.4.2", "pathe": "2.0.3", "yaml": "2.8.2" } }, "sha512-ZsJM+y9SOVjRL+g9Tr/jFEodcYPfHSt5ZRrpbeGDK71yTvSZw41A3UJ7Bg+RXM8JJWRYcJkYVkzrmbb/ig7qlA=="], + + "@scalar/openapi-types": ["@scalar/openapi-types@0.6.1", "", { "dependencies": { "zod": "4.3.6" } }, "sha512-P1RvyTFN0vRSL136OqWjlZfSFjY9JoJfuD6LM1mIjoocfwmqX3WuzsFEFX6hAeeDlTh6gjbiy+OdhSee8GFfSA=="], + + "@scalar/openapi-upgrader": ["@scalar/openapi-upgrader@0.2.2", "", { "dependencies": { "@scalar/openapi-types": "0.6.1" } }, "sha512-bIFfdiDyHVcUQHY2iTsmtDwhUHX09F/xCM5cGp1cVSbbahMiMmdKaxf/PnNsGSR5wGHfYknV1X1KS1brtXxrnw=="], + + "@scarf/scarf": ["@scarf/scarf@1.4.0", "", {}, "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ=="], + + "@shikijs/core": ["@shikijs/core@4.0.2", "", { "dependencies": { "@shikijs/primitive": "4.0.2", "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "10.0.2", "@types/hast": "3.0.4", "hast-util-to-html": "9.0.5" } }, "sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw=="], + + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "10.0.2", "oniguruma-to-es": "4.3.5" } }, "sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag=="], + + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "10.0.2" } }, "sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg=="], + + "@shikijs/langs": ["@shikijs/langs@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2" } }, "sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg=="], + + "@shikijs/primitive": ["@shikijs/primitive@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "10.0.2", "@types/hast": "3.0.4" } }, "sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw=="], + + "@shikijs/rehype": ["@shikijs/rehype@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@types/hast": "3.0.4", "hast-util-to-string": "3.0.1", "shiki": "4.0.2", "unified": "11.0.5", "unist-util-visit": "5.1.0" } }, "sha512-cmPlKLD8JeojasNFoY64162ScpEdEdQUMuVodPCrv1nx1z3bjmGwoKWDruQWa/ejSznImlaeB0Ty6Q3zPaVQAA=="], + + "@shikijs/themes": ["@shikijs/themes@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2" } }, "sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA=="], + + "@shikijs/transformers": ["@shikijs/transformers@4.0.2", "", { "dependencies": { "@shikijs/core": "4.0.2", "@shikijs/types": "4.0.2" } }, "sha512-1+L0gf9v+SdDXs08vjaLb3mBFa8U7u37cwcBQIv/HCocLwX69Tt6LpUCjtB+UUTvQxI7BnjZKhN/wMjhHBcJGg=="], + + "@shikijs/types": ["@shikijs/types@4.0.2", "", { "dependencies": { "@shikijs/vscode-textmate": "10.0.2", "@types/hast": "3.0.4" } }, "sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg=="], + + "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + + "@sideway/address": ["@sideway/address@4.1.5", "", { "dependencies": { "@hapi/hoek": "9.3.0" } }, "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q=="], + + "@sideway/formula": ["@sideway/formula@3.0.1", "", {}, "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg=="], + + "@sideway/pinpoint": ["@sideway/pinpoint@2.0.0", "", {}, "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ=="], + + "@smithy/core": ["@smithy/core@3.35.1", "", { "dependencies": { "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-i4YPS4B6ts7bjn7UwLnGjiZdprOvHvgGobFZsYK3GIY3E5hIqtj0rReU69BcTpGp+fvtraSNXeG1l+jtJvF55w=="], + + "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.5.2", "", { "dependencies": { "@smithy/core": "^3.33.2", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg=="], + + "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.8.0", "", { "dependencies": { "@smithy/core": "^3.33.3", "@smithy/types": "^4.18.0", "tslib": "^2.6.2" } }, "sha512-ycSJu3tFAQ4v04CBB0agqFMVsSQ1iG3yw+SpgxRqKfaURpQD4CZ8Wn0zPMmSnOuTpTh65Vz+EA0rMrw089wvkA=="], + + "@smithy/node-http-handler": ["@smithy/node-http-handler@4.12.1", "", { "dependencies": { "@smithy/core": "^3.33.3", "@smithy/types": "^4.18.0", "tslib": "^2.6.2" } }, "sha512-ThMkboGeONWXAelq9FvGsuJC4rOi+qyC4/zhUF58xYpxUg5sQKx2VXZYJmtNjr4dSuBJ1HeJXETQILCz3wOHvw=="], + + "@smithy/signature-v4": ["@smithy/signature-v4@5.7.4", "", { "dependencies": { "@smithy/core": "^3.35.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-tHy0K0VtqNd5Y7Y41h0a0Lhh0L1GzC08dTWg0F7vRJWFtTENg7IZikf3wQkanYIRdb7ngoIPMTmqgUi401fEeQ=="], + + "@smithy/types": ["@smithy/types@4.19.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-r7jh49VJxGerfAcTQA6gXcKc+98zOp/tqRwzYjgOE+iSQsP6cEU1hq2QzbuipmP68QtYdY9wKEhiCQZIzHgZ4Q=="], + + "@stablelib/base64": ["@stablelib/base64@1.0.1", "", {}, "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + + "@swc/helpers": ["@swc/helpers@0.5.23", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.1.4", "", { "dependencies": { "enhanced-resolve": "5.18.1", "jiti": "2.4.2", "lightningcss": "1.29.2", "tailwindcss": "4.1.4" } }, "sha512-MT5118zaiO6x6hNA04OWInuAiP1YISXql8Z+/Y8iisV5nuhM8VXlyhRuqc2PEviPszcXI66W44bCIk500Oolhw=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.4", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.4", "@tailwindcss/oxide-darwin-arm64": "4.1.4", "@tailwindcss/oxide-darwin-x64": "4.1.4", "@tailwindcss/oxide-freebsd-x64": "4.1.4", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.4", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.4", "@tailwindcss/oxide-linux-arm64-musl": "4.1.4", "@tailwindcss/oxide-linux-x64-gnu": "4.1.4", "@tailwindcss/oxide-linux-x64-musl": "4.1.4", "@tailwindcss/oxide-wasm32-wasi": "4.1.4", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.4", "@tailwindcss/oxide-win32-x64-msvc": "4.1.4" } }, "sha512-p5wOpXyOJx7mKh5MXh5oKk+kqcz8T+bA3z/5VWWeQwFrmuBItGwz8Y2CHk/sJ+dNb9B0nYFfn0rj/cKHZyjahQ=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.4", "", { "os": "android", "cpu": "arm64" }, "sha512-xMMAe/SaCN/vHfQYui3fqaBDEXMu22BVwQ33veLc8ep+DNy7CWN52L+TTG9y1K397w9nkzv+Mw+mZWISiqhmlA=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JGRj0SYFuDuAGilWFBlshcexev2hOKfNkoX+0QTksKYq2zgF9VY/vVMq9m8IObYnLna0Xlg+ytCi2FN2rOL0Sg=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-sdDeLNvs3cYeWsEJ4H1DvjOzaGios4QbBTNLVLVs0XQ0V95bffT3+scptzYGPMjm7xv4+qMhCDrkHwhnUySEzA=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-VHxAqxqdghM83HslPhRsNhHo91McsxRJaEnShJOMu8mHmEj9Ig7ToHJtDukkuLWLzLboh2XSjq/0zO6wgvykNA=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.4", "", { "os": "linux", "cpu": "arm" }, "sha512-OTU/m/eV4gQKxy9r5acuesqaymyeSCnsx1cFto/I1WhPmi5HDxX1nkzb8KYBiwkHIGg7CTfo/AcGzoXAJBxLfg=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-hKlLNvbmUC6z5g/J4H+Zx7f7w15whSVImokLPmP6ff1QqTVE+TxUM9PGuNsjHvkvlHUtGTdDnOvGNSEUiXI1Ww=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-X3As2xhtgPTY/m5edUtddmZ8rCruvBvtxYLMw9OsZdH01L2gS2icsHRwxdU0dMItNfVmrBezueXZCHxVeeb7Aw=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.4", "", { "os": "linux", "cpu": "x64" }, "sha512-2VG4DqhGaDSmYIu6C4ua2vSLXnJsb/C9liej7TuSO04NK+JJJgJucDUgmX6sn7Gw3Cs5ZJ9ZLrnI0QRDOjLfNQ=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.4", "", { "os": "linux", "cpu": "x64" }, "sha512-v+mxVgH2kmur/X5Mdrz9m7TsoVjbdYQT0b4Z+dr+I4RvreCNXyCFELZL/DO0M1RsidZTrm6O1eMnV6zlgEzTMQ=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.4", "", { "cpu": "none" }, "sha512-2TLe9ir+9esCf6Wm+lLWTMbgklIjiF0pbmDnwmhR9MksVOq+e8aP3TSsXySnBDDvTTVd/vKu1aNttEGj3P6l8Q=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-VlnhfilPlO0ltxW9/BgfLI5547PYzqBMPIzRrk4W7uupgCt8z6Trw/tAj6QUtF2om+1MH281Pg+HHUJoLesmng=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.4", "", { "os": "win32", "cpu": "x64" }, "sha512-+7S63t5zhYjslUGb8NcgLpFXD+Kq1F/zt5Xv5qTv7HaFTG/DHyHD9GA6ieNAxhgyA4IcKa/zy7Xx4Oad2/wuhw=="], + + "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.4", "", { "dependencies": { "@alloc/quick-lru": "5.2.0", "@tailwindcss/node": "4.1.4", "@tailwindcss/oxide": "4.1.4", "postcss": "8.5.3", "tailwindcss": "4.1.4" } }, "sha512-bjV6sqycCEa+AQSt2Kr7wpGF1bOZJ5wsqnLEkqSbM/JEHxx/yhMH8wHmdkPyApF9xhHeMSwnnkDUUMMM/hYnXw=="], + + "@tus/file-store": ["@tus/file-store@2.1.1", "", { "dependencies": { "@tus/utils": "^0.7.1", "debug": "^4.3.4" }, "optionalDependencies": { "@redis/client": "^5.0.0" } }, "sha512-/uXV3Ibb4Upirrsj7lSrX+o08H7IKYEkPHV8TKAPPK6CYUcOMTilluOkwPrb9JZydLSYlIBQkhZZVS9sPEfDwQ=="], + + "@tus/server": ["@tus/server@2.4.5", "", { "dependencies": { "@tus/utils": "^0.7.1", "debug": "^4.3.4", "lodash.throttle": "^4.1.1", "set-cookie-parser": "^2.7.1", "srvx": "~0.11.15" }, "optionalDependencies": { "@redis/client": "^5.0.0", "ioredis": "^5.4.1" } }, "sha512-DaQY5F1/JC1E+eDNY+Q5L08wgQ8NHFDNW/E6NclSx10BOHJk85BT4mSId49Ep890Utsk+6hEEuhNFb/LH+Og0w=="], + + "@tus/utils": ["@tus/utils@0.7.1", "", {}, "sha512-KXKK6mhsRto7cApRtJFoIovd6rAXJBUd1qR6S1ynVXApdd+jT1eBHprKP0D9oZtJwwD/B8+n3DLAm68DYPqfuQ=="], + + "@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="], + + "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "7.27.0", "@babel/types": "7.27.0", "@types/babel__generator": "7.27.0", "@types/babel__template": "7.4.4", "@types/babel__traverse": "7.20.7" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], + + "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "7.27.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], + + "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "7.27.0", "@babel/types": "7.27.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="], + + "@types/babel__traverse": ["@types/babel__traverse@7.20.7", "", { "dependencies": { "@babel/types": "7.27.0" } }, "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng=="], + + "@types/body-parser": ["@types/body-parser@1.19.5", "", { "dependencies": { "@types/connect": "3.4.38", "@types/node": "20.10.6" } }, "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg=="], + + "@types/busboy": ["@types/busboy@1.5.4", "", { "dependencies": { "@types/node": "20.10.6" } }, "sha512-kG7WrUuAKK0NoyxfQHsVE6j1m01s6kMma64E+OZenQABMQyTJop1DumUWcLwAQ2JzpefU7PDYoRDKl8uZosFjw=="], + + "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "20.10.6" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], + + "@types/cors": ["@types/cors@2.8.17", "", { "dependencies": { "@types/node": "20.10.6" } }, "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA=="], + + "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "2.1.0" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "1.0.7" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="], + + "@types/express": ["@types/express@4.17.21", "", { "dependencies": { "@types/body-parser": "1.19.5", "@types/express-serve-static-core": "4.19.6", "@types/qs": "6.9.18", "@types/serve-static": "1.15.7" } }, "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ=="], + + "@types/express-fileupload": ["@types/express-fileupload@1.5.1", "", { "dependencies": { "@types/busboy": "1.5.4", "@types/express": "4.17.21" } }, "sha512-DllImBVI1lCyjl2klky/TEwk60mbNebgXv1669h66g9TfptWSrEFq5a/raHSutaFzjSm1tmn9ypdNfu4jPSixQ=="], + + "@types/express-serve-static-core": ["@types/express-serve-static-core@4.19.6", "", { "dependencies": { "@types/node": "20.10.6", "@types/qs": "6.9.18", "@types/range-parser": "1.2.7", "@types/send": "0.17.4" } }, "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A=="], + + "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "3.0.3" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], + + "@types/http-errors": ["@types/http-errors@2.0.4", "", {}, "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA=="], + + "@types/istanbul-lib-coverage": ["@types/istanbul-lib-coverage@2.0.6", "", {}, "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w=="], + + "@types/istanbul-lib-report": ["@types/istanbul-lib-report@3.0.3", "", { "dependencies": { "@types/istanbul-lib-coverage": "2.0.6" } }, "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA=="], + + "@types/istanbul-reports": ["@types/istanbul-reports@1.1.2", "", { "dependencies": { "@types/istanbul-lib-coverage": "2.0.6", "@types/istanbul-lib-report": "3.0.3" } }, "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="], + + "@types/jsonwebtoken": ["@types/jsonwebtoken@9.0.10", "", { "dependencies": { "@types/ms": "2.1.0", "@types/node": "20.10.6" } }, "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA=="], + + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "3.0.3" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], + + "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], + + "@types/mime": ["@types/mime@1.3.5", "", {}, "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="], + + "@types/mime-types": ["@types/mime-types@3.0.1", "", {}, "sha512-xRMsfuQbnRq1Ef+C+RKaENOxXX87Ygl38W1vDfPHRku02TgQr+Qd8iivLtAMcR0KF5/29xlnFihkTlbqFrGOVQ=="], + + "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], + + "@types/node": ["@types/node@20.10.6", "", { "dependencies": { "undici-types": "5.26.5" } }, "sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw=="], + + "@types/nodemailer": ["@types/nodemailer@6.4.17", "", { "dependencies": { "@types/node": "20.10.6" } }, "sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww=="], + + "@types/pg": ["@types/pg@8.23.1", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-fKVHpikPdg4GKks3JuLEhvwSyvwzF23hnabPy6DD8ljVbC7+6J5dQzdv4arV6jqq57djnMgs1HKBxX4P8aBI3A=="], + + "@types/qs": ["@types/qs@6.9.18", "", {}, "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA=="], + + "@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="], + + "@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "3.1.3" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="], + + "@types/react-dom": ["@types/react-dom@19.2.2", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw=="], + + "@types/send": ["@types/send@0.17.4", "", { "dependencies": { "@types/mime": "1.3.5", "@types/node": "20.10.6" } }, "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA=="], + + "@types/serve-static": ["@types/serve-static@1.15.7", "", { "dependencies": { "@types/http-errors": "2.0.4", "@types/node": "20.10.6", "@types/send": "0.17.4" } }, "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw=="], + + "@types/stack-utils": ["@types/stack-utils@1.0.1", "", {}, "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw=="], + + "@types/swagger-ui-express": ["@types/swagger-ui-express@4.1.8", "", { "dependencies": { "@types/express": "4.17.21", "@types/serve-static": "1.15.7" } }, "sha512-AhZV8/EIreHFmBV5wAs0gzJUNq9JbbSXgJLQubCC0jtIo6prnI9MIRRxnU4MZX9RB9yXxF1V4R7jtLl/Wcj31g=="], + + "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], + + "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + + "@types/webidl-conversions": ["@types/webidl-conversions@7.0.3", "", {}, "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="], + + "@types/whatwg-url": ["@types/whatwg-url@11.0.5", "", { "dependencies": { "@types/webidl-conversions": "7.0.3" } }, "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ=="], + + "@types/yargs": ["@types/yargs@13.0.12", "", { "dependencies": { "@types/yargs-parser": "21.0.3" } }, "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ=="], + + "@types/yargs-parser": ["@types/yargs-parser@21.0.3", "", {}, "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.29.1", "", { "dependencies": { "@eslint-community/regexpp": "4.12.1", "@typescript-eslint/scope-manager": "8.29.1", "@typescript-eslint/type-utils": "8.29.1", "@typescript-eslint/utils": "8.29.1", "@typescript-eslint/visitor-keys": "8.29.1", "graphemer": "1.4.0", "ignore": "5.3.2", "natural-compare": "1.4.0", "ts-api-utils": "2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-ba0rr4Wfvg23vERs3eB+P3lfj2E+2g3lhWcCVukUuhtcdUx5lSIFZlGFEBHKr+3zizDa/TvZTptdNHVZWAkSBg=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.29.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.29.1", "@typescript-eslint/types": "8.29.1", "@typescript-eslint/typescript-estree": "8.29.1", "@typescript-eslint/visitor-keys": "8.29.1", "debug": "4.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-zczrHVEqEaTwh12gWBIJWj8nx+ayDcCJs06yoNMY0kwjMWDM6+kppljY+BxWI06d2Ja+h4+WdufDcwMnnMEWmg=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.29.1", "", { "dependencies": { "@typescript-eslint/types": "8.29.1", "@typescript-eslint/visitor-keys": "8.29.1" } }, "sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.29.1", "", { "dependencies": { "@typescript-eslint/typescript-estree": "8.29.1", "@typescript-eslint/utils": "8.29.1", "debug": "4.4.0", "ts-api-utils": "2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-DkDUSDwZVCYN71xA4wzySqqcZsHKic53A4BLqmrWFFpOpNSoxX233lwGu/2135ymTCR04PoKiEEEvN1gFYg4Tw=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.29.1", "", {}, "sha512-VT7T1PuJF1hpYC3AGm2rCgJBjHL3nc+A/bhOp9sGMKfi5v0WufsX/sHCFBfNTx2F+zA6qBc/PD0/kLRLjdt8mQ=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.29.1", "", { "dependencies": { "@typescript-eslint/types": "8.29.1", "@typescript-eslint/visitor-keys": "8.29.1", "debug": "4.4.0", "fast-glob": "3.3.3", "is-glob": "4.0.3", "minimatch": "9.0.5", "semver": "7.7.3", "ts-api-utils": "2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.29.1", "", { "dependencies": { "@eslint-community/eslint-utils": "4.5.1", "@typescript-eslint/scope-manager": "8.29.1", "@typescript-eslint/types": "8.29.1", "@typescript-eslint/typescript-estree": "8.29.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.29.1", "", { "dependencies": { "@typescript-eslint/types": "8.29.1", "eslint-visitor-keys": "4.2.0" } }, "sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg=="], + + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + + "@unrs/resolver-binding-darwin-arm64": ["@unrs/resolver-binding-darwin-arm64@1.4.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-8Tv+Bsd0BjGwfEedIyor4inw8atppRxM5BdUnIt+3mAm/QXUm7Dw74CHnXpfZKXkp07EXJGiA8hStqCINAWhdw=="], + + "@unrs/resolver-binding-darwin-x64": ["@unrs/resolver-binding-darwin-x64@1.4.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-X8c3PhWziEMKAzZz+YAYWfwawi5AEgzy/hmfizAB4C70gMHLKmInJcp1270yYAOs7z07YVFI220pp50z24Jk3A=="], + + "@unrs/resolver-binding-freebsd-x64": ["@unrs/resolver-binding-freebsd-x64@1.4.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-UUr/nREy1UdtxXQnmLaaTXFGOcGxPwNIzeJdb3KXai3TKtC1UgNOB9s8KOA4TaxOUBR/qVgL5BvBwmUjD5yuVA=="], + + "@unrs/resolver-binding-linux-arm-gnueabihf": ["@unrs/resolver-binding-linux-arm-gnueabihf@1.4.1", "", { "os": "linux", "cpu": "arm" }, "sha512-e3pII53dEeS8inkX6A1ad2UXE0nuoWCqik4kOxaDnls0uJUq0ntdj5d9IYd+bv5TDwf9DSge/xPOvCmRYH+Tsw=="], + + "@unrs/resolver-binding-linux-arm-musleabihf": ["@unrs/resolver-binding-linux-arm-musleabihf@1.4.1", "", { "os": "linux", "cpu": "arm" }, "sha512-e/AKKd9gR+HNmVyDEPI/PIz2t0DrA3cyonHNhHVjrkxe8pMCiYiqhtn1+h+yIpHUtUlM6Y1FNIdivFa+r7wrEQ=="], + + "@unrs/resolver-binding-linux-arm64-gnu": ["@unrs/resolver-binding-linux-arm64-gnu@1.4.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-vtIu34luF1jRktlHtiwm2mjuE8oJCsFiFr8hT5+tFQdqFKjPhbJXn83LswKsOhy0GxAEevpXDI4xxEwkjuXIPA=="], + + "@unrs/resolver-binding-linux-arm64-musl": ["@unrs/resolver-binding-linux-arm64-musl@1.4.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-H3PaOuGyhFXiyJd+09uPhGl4gocmhyi1BRzvsP8Lv5AQO3p3/ZY7WjV4t2NkBksm9tMjf3YbOVHyPWi2eWsNYw=="], + + "@unrs/resolver-binding-linux-ppc64-gnu": ["@unrs/resolver-binding-linux-ppc64-gnu@1.4.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-4+GmJcaaFntCi1S01YByqp8wLMjV/FyQyHVGm0vedIhL1Vfx7uHkz/sZmKsidRwokBGuxi92GFmSzqT2O8KcNA=="], + + "@unrs/resolver-binding-linux-s390x-gnu": ["@unrs/resolver-binding-linux-s390x-gnu@1.4.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-6RDQVCmtFYTlhy89D5ixTqo9bTQqFhvNN0Ey1wJs5r+01Dq15gPHRXv2jF2bQATtMrOfYwv+R2ZR9ew1N1N3YQ=="], + + "@unrs/resolver-binding-linux-x64-gnu": ["@unrs/resolver-binding-linux-x64-gnu@1.4.1", "", { "os": "linux", "cpu": "x64" }, "sha512-XpU9uzIkD86+19NjCXxlVPISMUrVXsXo5htxtuG+uJ59p5JauSRZsIxQxzzfKzkxEjdvANPM/lS1HFoX6A6QeA=="], + + "@unrs/resolver-binding-linux-x64-musl": ["@unrs/resolver-binding-linux-x64-musl@1.4.1", "", { "os": "linux", "cpu": "x64" }, "sha512-3CDjG/spbTKCSHl66QP2ekHSD+H34i7utuDIM5gzoNBcZ1gTO0Op09Wx5cikXnhORRf9+HyDWzm37vU1PLSM1A=="], + + "@unrs/resolver-binding-wasm32-wasi": ["@unrs/resolver-binding-wasm32-wasi@1.4.1", "", { "dependencies": { "@napi-rs/wasm-runtime": "0.2.8" }, "cpu": "none" }, "sha512-50tYhvbCTnuzMn7vmP8IV2UKF7ITo1oihygEYq9wW2DUb/Y+QMqBHJUSCABRngATjZ4shOK6f2+s0gQX6ElENQ=="], + + "@unrs/resolver-binding-win32-arm64-msvc": ["@unrs/resolver-binding-win32-arm64-msvc@1.4.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-KyJiIne/AqV4IW0wyQO34wSMuJwy3VxVQOfIXIPyQ/Up6y/zi2P/WwXb78gHsLiGRUqCA9LOoCX+6dQZde0g1g=="], + + "@unrs/resolver-binding-win32-ia32-msvc": ["@unrs/resolver-binding-win32-ia32-msvc@1.4.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-y2NUD7pygrBolN2NoXUrwVqBpKPhF8DiSNE5oB5/iFO49r2DpoYqdj5HPb3F42fPBH5qNqj6Zg63+xCEzAD2hw=="], + + "@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.4.1", "", { "os": "win32", "cpu": "x64" }, "sha512-hVXaObGI2lGFmrtT77KSbPQ3I+zk9IU500wobjk0+oX59vg/0VqAzABNtt3YSQYgXTC2a/LYxekLfND/wlt0yQ=="], + + "abab": ["abab@2.0.6", "", {}, "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="], + + "accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "2.1.35", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], + + "acorn": ["acorn@7.4.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="], + + "acorn-globals": ["acorn-globals@4.3.4", "", { "dependencies": { "acorn": "6.4.2", "acorn-walk": "6.2.0" } }, "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "acorn-walk": ["acorn-walk@6.2.0", "", {}, "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA=="], + + "ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="], + + "ajv-formats": ["ajv-formats@3.0.1", "", { "peerDependencies": { "ajv": "^8.0.0" }, "optionalPeers": ["ajv"] }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], + + "ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="], + + "ansi-escapes": ["ansi-escapes@7.0.0", "", { "dependencies": { "environment": "1.1.0" } }, "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw=="], + + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], + + "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "3.0.0", "picomatch": "2.3.1" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], + + "anynum": ["anynum@1.0.1", "", {}, "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A=="], + + "append-field": ["append-field@1.0.0", "", {}, "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="], + + "arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "aria-hidden": ["aria-hidden@1.2.4", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A=="], + + "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], + + "arr-diff": ["arr-diff@4.0.0", "", {}, "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA=="], + + "arr-flatten": ["arr-flatten@1.1.0", "", {}, "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg=="], + + "arr-union": ["arr-union@3.1.0", "", {}, "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q=="], + + "array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "1.0.4", "is-array-buffer": "3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="], + + "array-equal": ["array-equal@1.0.2", "", {}, "sha512-gUHx76KtnhEgB3HOuFYiCm3FIdEs6ocM2asHvNTkfu/Y09qQVrrVVaOKENmS2KkSaGoxgXNqC+ZVtR/n0MOkSA=="], + + "array-flatten": ["array-flatten@1.1.1", "", {}, "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="], + + "array-includes": ["array-includes@3.1.8", "", { "dependencies": { "call-bind": "1.0.8", "define-properties": "1.2.1", "es-abstract": "1.23.9", "es-object-atoms": "1.1.1", "get-intrinsic": "1.3.0", "is-string": "1.1.1" } }, "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ=="], + + "array-union": ["array-union@2.1.0", "", {}, "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="], + + "array-unique": ["array-unique@0.3.2", "", {}, "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ=="], + + "array.prototype.findlast": ["array.prototype.findlast@1.2.5", "", { "dependencies": { "call-bind": "1.0.8", "define-properties": "1.2.1", "es-abstract": "1.23.9", "es-errors": "1.3.0", "es-object-atoms": "1.1.1", "es-shim-unscopables": "1.1.0" } }, "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ=="], + + "array.prototype.findlastindex": ["array.prototype.findlastindex@1.2.6", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "define-properties": "1.2.1", "es-abstract": "1.23.9", "es-errors": "1.3.0", "es-object-atoms": "1.1.1", "es-shim-unscopables": "1.1.0" } }, "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ=="], + + "array.prototype.flat": ["array.prototype.flat@1.3.3", "", { "dependencies": { "call-bind": "1.0.8", "define-properties": "1.2.1", "es-abstract": "1.23.9", "es-shim-unscopables": "1.1.0" } }, "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg=="], + + "array.prototype.flatmap": ["array.prototype.flatmap@1.3.3", "", { "dependencies": { "call-bind": "1.0.8", "define-properties": "1.2.1", "es-abstract": "1.23.9", "es-shim-unscopables": "1.1.0" } }, "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg=="], + + "array.prototype.reduce": ["array.prototype.reduce@1.0.8", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "define-properties": "1.2.1", "es-abstract": "1.23.9", "es-array-method-boxes-properly": "1.0.0", "es-errors": "1.3.0", "es-object-atoms": "1.1.1", "is-string": "1.1.1" } }, "sha512-DwuEqgXFBwbmZSRqt3BpQigWNUoqw9Ml2dTWdF3B2zQlQX4OeUE0zyuzX0fX0IbTvjdkZbcBTU3idgpO78qkTw=="], + + "array.prototype.tosorted": ["array.prototype.tosorted@1.1.4", "", { "dependencies": { "call-bind": "1.0.8", "define-properties": "1.2.1", "es-abstract": "1.23.9", "es-errors": "1.3.0", "es-shim-unscopables": "1.1.0" } }, "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA=="], + + "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "1.0.2", "call-bind": "1.0.8", "define-properties": "1.2.1", "es-abstract": "1.23.9", "es-errors": "1.3.0", "get-intrinsic": "1.3.0", "is-array-buffer": "3.0.5" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="], + + "asn1": ["asn1@0.2.6", "", { "dependencies": { "safer-buffer": "2.1.2" } }, "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ=="], + + "assert-plus": ["assert-plus@1.0.0", "", {}, "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="], + + "assign-symbols": ["assign-symbols@1.0.0", "", {}, "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw=="], + + "ast-types-flow": ["ast-types-flow@0.0.8", "", {}, "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ=="], + + "astral-regex": ["astral-regex@1.0.0", "", {}, "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg=="], + + "astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="], + + "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="], + + "async-limiter": ["async-limiter@1.0.1", "", {}, "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="], + + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + + "atob": ["atob@2.1.2", "", { "bin": { "atob": "bin/atob.js" } }, "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="], + + "atomic-sleep": ["atomic-sleep@1.0.0", "", {}, "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="], + + "autoprefixer": ["autoprefixer@10.4.21", "", { "dependencies": { "browserslist": "4.24.4", "caniuse-lite": "1.0.30001712", "fraction.js": "4.3.7", "normalize-range": "0.1.2", "picocolors": "1.1.1", "postcss-value-parser": "4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ=="], + + "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "1.1.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], + + "aws-sign2": ["aws-sign2@0.7.0", "", {}, "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA=="], + + "aws4": ["aws4@1.13.2", "", {}, "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw=="], + + "axe-core": ["axe-core@4.10.3", "", {}, "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg=="], + + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], + + "babel-jest": ["babel-jest@24.9.0", "", { "dependencies": { "@jest/transform": "24.9.0", "@jest/types": "24.9.0", "@types/babel__core": "7.20.5", "babel-plugin-istanbul": "5.2.0", "babel-preset-jest": "24.9.0", "chalk": "2.4.2", "slash": "2.0.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw=="], + + "babel-plugin-istanbul": ["babel-plugin-istanbul@5.2.0", "", { "dependencies": { "@babel/helper-plugin-utils": "7.26.5", "find-up": "3.0.0", "istanbul-lib-instrument": "3.3.0", "test-exclude": "5.2.3" } }, "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw=="], + + "babel-plugin-jest-hoist": ["babel-plugin-jest-hoist@24.9.0", "", { "dependencies": { "@types/babel__traverse": "7.20.7" } }, "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw=="], + + "babel-preset-jest": ["babel-preset-jest@24.9.0", "", { "dependencies": { "@babel/plugin-syntax-object-rest-spread": "7.8.3", "babel-plugin-jest-hoist": "24.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg=="], + + "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "base": ["base@0.11.2", "", { "dependencies": { "cache-base": "1.0.1", "class-utils": "0.3.6", "component-emitter": "1.3.1", "define-property": "1.0.0", "isobject": "3.0.1", "mixin-deep": "1.3.2", "pascalcase": "0.1.1" } }, "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.11.0", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-oCu2wfipvX3AePSgmOuKkIywOu+8n9psz7hXYmk56ghpu3+7KzNIBopaOs4c9BrtdnTtW30unG9GTfHo7EwERQ=="], + + "bcrypt-pbkdf": ["bcrypt-pbkdf@1.0.2", "", { "dependencies": { "tweetnacl": "0.14.5" } }, "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w=="], + + "better-auth": ["better-auth@1.7.7", "", { "dependencies": { "@better-auth/core": "1.7.7", "@better-auth/drizzle-adapter": "1.7.7", "@better-auth/kysely-adapter": "1.7.7", "@better-auth/memory-adapter": "1.7.7", "@better-auth/mongo-adapter": "1.7.7", "@better-auth/prisma-adapter": "1.7.7", "@better-auth/telemetry": "1.7.7", "@better-auth/utils": "0.4.2", "@better-fetch/fetch": "1.3.2", "@noble/ciphers": "^2.2.0", "@noble/hashes": "^2.2.0", "better-call": "1.4.0", "defu": "^6.1.4", "jose": "^6.2.3", "kysely": "^0.28.17 || ^0.29.0", "nanostores": "^1.3.0", "zod": "^4.5.4" }, "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", "drizzle-kit": ">=0.31.4 || >=1.0.0-beta.1", "drizzle-orm": "^0.45.2 || >=1.0.0-rc.1 <2.0.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 || ^5.0.0", "vue": "^3.0.0" }, "optionalPeers": ["@lynx-js/react", "@prisma/client", "@sveltejs/kit", "@tanstack/react-start", "@tanstack/solid-start", "drizzle-kit", "drizzle-orm", "mongodb", "mysql2", "next", "pg", "prisma", "react", "react-dom", "solid-js", "svelte", "vitest", "vue"] }, "sha512-Zhgxi/c5ylmfB/sX+nwnBbsFf/j6k0L8DvL6CJXZSrXgxc5pPMC/e7j812zNd4L4B4ELmdURCPw3X8nAOUdcjw=="], + + "better-call": ["better-call@1.4.0", "", { "dependencies": { "@better-auth/utils": "^0.5.0", "@better-fetch/fetch": "^1.3.1", "rou3": "^0.9.1", "set-cookie-parser": "^3.1.2" }, "peerDependencies": { "zod": "^4.0.0" }, "optionalPeers": ["zod"] }, "sha512-bBKOT4vv1kZLDgxVePdilk/Jwkn+dtRRsmi3DzHcDP+WnswyVl6dR59l2HEeP/0cB+bDoopASAesWDPIdd/zZA=="], + + "better-path-resolve": ["better-path-resolve@1.0.0", "", { "dependencies": { "is-windows": "1.0.2" } }, "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g=="], + + "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], + + "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="], + + "body-parser": ["body-parser@1.20.6", "", { "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "~1.2.0", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", "qs": "~6.15.1", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" } }, "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g=="], + + "bowser": ["bowser@2.11.0", "", {}, "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA=="], + + "brace-expansion": ["brace-expansion@1.1.21", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "browser-process-hrtime": ["browser-process-hrtime@1.0.0", "", {}, "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow=="], + + "browser-resolve": ["browser-resolve@1.11.3", "", { "dependencies": { "resolve": "1.1.7" } }, "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ=="], + + "browserslist": ["browserslist@4.28.7", "", { "dependencies": { "baseline-browser-mapping": "^2.10.44", "caniuse-lite": "^1.0.30001806", "electron-to-chromium": "^1.5.393", "node-releases": "^2.0.51", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw=="], + + "bser": ["bser@2.1.1", "", { "dependencies": { "node-int64": "0.4.0" } }, "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ=="], + + "bson": ["bson@6.10.4", "", {}, "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng=="], + + "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], + + "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], + + "bundle-require": ["bundle-require@5.1.0", "", { "dependencies": { "load-tsconfig": "0.2.5" }, "peerDependencies": { "esbuild": ">=0.18" } }, "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA=="], + + "busboy": ["busboy@1.6.0", "", { "dependencies": { "streamsearch": "1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="], + + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + + "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], + + "cache-base": ["cache-base@1.0.1", "", { "dependencies": { "collection-visit": "1.0.0", "component-emitter": "1.3.1", "get-value": "2.0.6", "has-value": "1.0.0", "isobject": "3.0.1", "set-value": "2.0.1", "to-object-path": "0.3.0", "union-value": "1.0.1", "unset-value": "1.0.0" } }, "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ=="], + + "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "1.0.2", "es-define-property": "1.0.1", "get-intrinsic": "1.3.0", "set-function-length": "1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "1.3.0", "function-bind": "1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "1.0.2", "get-intrinsic": "1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="], + + "camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001712", "", {}, "sha512-MBqPpGYYdQ7/hfKiet9SCI+nmN5/hp4ZzveOJubl5DTAMa5oggjAuoi0Z4onBpKPFI2ePGnQuQIzF3VxDjDJig=="], + + "capture-exit": ["capture-exit@2.0.0", "", { "dependencies": { "rsvp": "4.8.5" } }, "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g=="], + + "caseless": ["caseless@0.12.0", "", {}, "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="], + + "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "4.3.0", "supports-color": "7.2.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], + + "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], + + "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], + + "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="], + + "chardet": ["chardet@0.7.0", "", {}, "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="], + + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "4.1.2" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + + "ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "class-utils": ["class-utils@0.3.6", "", { "dependencies": { "arr-union": "3.1.0", "define-property": "0.2.5", "isobject": "3.0.1", "static-extend": "0.1.2" } }, "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg=="], + + "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], + + "cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "5.1.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], + + "cli-truncate": ["cli-truncate@4.0.0", "", { "dependencies": { "slice-ansi": "5.0.0", "string-width": "7.2.0" } }, "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA=="], + + "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], + + "cliui": ["cliui@5.0.0", "", { "dependencies": { "string-width": "3.1.0", "strip-ansi": "5.2.0", "wrap-ansi": "5.1.0" } }, "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="], + + "co": ["co@4.6.0", "", {}, "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ=="], + + "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="], + + "collection-visit": ["collection-visit@1.0.0", "", { "dependencies": { "map-visit": "1.0.0", "object-visit": "1.0.1" } }, "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="], + + "combine-errors": ["combine-errors@3.0.3", "", { "dependencies": { "custom-error-instance": "2.1.1", "lodash.uniqby": "4.5.0" } }, "sha512-C8ikRNRMygCwaTx+Ek3Yr+OuZzgZjduCOfSQBjbM8V3MfgcjSTeto/GXP6PAwKvJz/v15b7GHZvx5rOlczFw/Q=="], + + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + + "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], + + "commander": ["commander@13.1.0", "", {}, "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw=="], + + "component-emitter": ["component-emitter@1.3.1", "", {}, "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ=="], + + "compute-scroll-into-view": ["compute-scroll-into-view@3.1.1", "", {}, "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "concat-stream": ["concat-stream@1.6.2", "", { "dependencies": { "buffer-from": "1.1.2", "inherits": "2.0.4", "readable-stream": "2.3.8", "typedarray": "0.0.6" } }, "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw=="], + + "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], + + "content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + + "cookie": ["cookie@0.7.1", "", {}, "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="], + + "cookie-signature": ["cookie-signature@1.0.6", "", {}, "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="], + + "copy-descriptor": ["copy-descriptor@0.1.1", "", {}, "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw=="], + + "core-js": ["core-js@3.50.0", "", {}, "sha512-BRWgOLKkFeCgRudR6zrs8p9XJZcE14grzKMMssoYrk6krtuEZ7MTKPIY5RzOnqsEKIR9kst7wNzphttraT+Yqw=="], + + "core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="], + + "cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "4.1.1", "vary": "1.1.2" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "3.1.1", "shebang-command": "2.0.0", "which": "2.0.2" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + + "cssom": ["cssom@0.3.8", "", {}, "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="], + + "cssstyle": ["cssstyle@1.4.0", "", { "dependencies": { "cssom": "0.3.8" } }, "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA=="], + + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + + "custom-error-instance": ["custom-error-instance@2.1.1", "", {}, "sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg=="], + + "damerau-levenshtein": ["damerau-levenshtein@1.0.8", "", {}, "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="], + + "dashdash": ["dashdash@1.14.1", "", { "dependencies": { "assert-plus": "1.0.0" } }, "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g=="], + + "data-urls": ["data-urls@1.1.0", "", { "dependencies": { "abab": "2.0.6", "whatwg-mimetype": "2.3.0", "whatwg-url": "7.1.0" } }, "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ=="], + + "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "1.0.4", "es-errors": "1.3.0", "is-data-view": "1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="], + + "data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "1.0.4", "es-errors": "1.3.0", "is-data-view": "1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="], + + "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "1.0.4", "es-errors": "1.3.0", "is-data-view": "1.0.2" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "decamelize": ["decamelize@1.2.0", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="], + + "decode-named-character-reference": ["decode-named-character-reference@1.1.0", "", { "dependencies": { "character-entities": "2.0.2" } }, "sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w=="], + + "decode-uri-component": ["decode-uri-component@0.2.2", "", {}, "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="], + + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + + "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], + + "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "1.0.1", "es-errors": "1.3.0", "gopd": "1.2.0" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], + + "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "1.1.4", "has-property-descriptors": "1.0.2", "object-keys": "1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], + + "define-property": ["define-property@2.0.2", "", { "dependencies": { "is-descriptor": "1.0.3", "isobject": "3.0.1" } }, "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ=="], + + "defu": ["defu@6.1.7", "", {}, "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ=="], + + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + + "denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="], + + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "dereference-json-schema": ["dereference-json-schema@0.2.2", "", {}, "sha512-w8dUsJyrzH4Zsj8W/tKcjLsmcTKXfdNf+n3BBm1SAfnqpaCodgEUWqQGJ+pNb9NOqPwYMGvUnZZ8nQfeFjJlbQ=="], + + "destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="], + + "detect-indent": ["detect-indent@6.1.0", "", {}, "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "detect-newline": ["detect-newline@2.1.0", "", {}, "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg=="], + + "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], + + "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "2.0.3" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], + + "didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="], + + "diff-sequences": ["diff-sequences@24.9.0", "", {}, "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew=="], + + "dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="], + + "dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="], + + "doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "2.0.3" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="], + + "dodopayments": ["dodopayments@2.48.0", "", { "dependencies": { "standardwebhooks": "^1.0.0" }, "bin": { "dodopayments": "bin/cli" } }, "sha512-MDsHBdhQuMgYmIykI+2IJeATGNXM7bJVFs4FBUAhmpLNjnxZEqFlmnpUHkWQI6kIHpWhZZXC1cCKpGJVBU6tfg=="], + + "domexception": ["domexception@1.0.1", "", { "dependencies": { "webidl-conversions": "4.0.2" } }, "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug=="], + + "dompurify": ["dompurify@3.4.16", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-sqo+pNp3qRhCIpbgRi1y8Tgk27Bo2Ry7w0dC1NBeNTdZChWjz9Xb/KOoZbRP/R6pQZ80Qw8YhXw13hWWBbMRnQ=="], + + "dotenv": ["dotenv@17.2.3", "", {}, "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w=="], + + "drizzle-kit": ["drizzle-kit@0.31.10", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "tsx": "^4.21.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-7OZcmQUrdGI+DUNNsKBn1aW8qSoKuTH7d0mYgSP8bAzdFzKoovxEFnoGQp2dVs82EOJeYycqRtciopszwUf8bw=="], + + "drizzle-orm": ["drizzle-orm@0.45.2", "", { "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" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "prisma", "sql.js", "sqlite3"] }, "sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "1.0.2", "es-errors": "1.3.0", "gopd": "1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + + "ecc-jsbn": ["ecc-jsbn@0.1.2", "", { "dependencies": { "jsbn": "0.1.1", "safer-buffer": "2.1.2" } }, "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw=="], + + "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], + + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + + "electron-to-chromium": ["electron-to-chromium@1.5.444", "", {}, "sha512-5ss/uJfoDYDHT0lfJzT6FbcskIzROIOPf0BbbFkGcvDzoJU7i//9GDrwwIHQVmIsrAGiF3ihpADBRIsrEFt1rQ=="], + + "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + + "end-of-stream": ["end-of-stream@1.4.4", "", { "dependencies": { "once": "1.4.0" } }, "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q=="], + + "enhanced-resolve": ["enhanced-resolve@5.18.1", "", { "dependencies": { "graceful-fs": "4.2.11", "tapable": "2.2.1" } }, "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg=="], + + "enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "4.1.3", "strip-ansi": "6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="], + + "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + + "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], + + "error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="], + + "es-abstract": ["es-abstract@1.23.9", "", { "dependencies": { "array-buffer-byte-length": "1.0.2", "arraybuffer.prototype.slice": "1.0.4", "available-typed-arrays": "1.0.7", "call-bind": "1.0.8", "call-bound": "1.0.4", "data-view-buffer": "1.0.2", "data-view-byte-length": "1.0.2", "data-view-byte-offset": "1.0.1", "es-define-property": "1.0.1", "es-errors": "1.3.0", "es-object-atoms": "1.1.1", "es-set-tostringtag": "2.1.0", "es-to-primitive": "1.3.0", "function.prototype.name": "1.1.8", "get-intrinsic": "1.3.0", "get-proto": "1.0.1", "get-symbol-description": "1.1.0", "globalthis": "1.0.4", "gopd": "1.2.0", "has-property-descriptors": "1.0.2", "has-proto": "1.2.0", "has-symbols": "1.1.0", "hasown": "2.0.2", "internal-slot": "1.1.0", "is-array-buffer": "3.0.5", "is-callable": "1.2.7", "is-data-view": "1.0.2", "is-regex": "1.2.1", "is-shared-array-buffer": "1.0.4", "is-string": "1.1.1", "is-typed-array": "1.1.15", "is-weakref": "1.1.1", "math-intrinsics": "1.1.0", "object-inspect": "1.13.4", "object-keys": "1.1.1", "object.assign": "4.1.7", "own-keys": "1.0.1", "regexp.prototype.flags": "1.5.4", "safe-array-concat": "1.1.3", "safe-push-apply": "1.0.0", "safe-regex-test": "1.1.0", "set-proto": "1.0.0", "string.prototype.trim": "1.2.10", "string.prototype.trimend": "1.0.9", "string.prototype.trimstart": "1.0.8", "typed-array-buffer": "1.0.3", "typed-array-byte-length": "1.0.3", "typed-array-byte-offset": "1.0.4", "typed-array-length": "1.0.7", "unbox-primitive": "1.1.0", "which-typed-array": "1.1.19" } }, "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA=="], + + "es-array-method-boxes-properly": ["es-array-method-boxes-properly@1.0.0", "", {}, "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-iterator-helpers": ["es-iterator-helpers@1.2.1", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "define-properties": "1.2.1", "es-abstract": "1.23.9", "es-errors": "1.3.0", "es-set-tostringtag": "2.1.0", "function-bind": "1.1.2", "get-intrinsic": "1.3.0", "globalthis": "1.0.4", "gopd": "1.2.0", "has-property-descriptors": "1.0.2", "has-proto": "1.2.0", "has-symbols": "1.1.0", "internal-slot": "1.1.0", "iterator.prototype": "1.1.5", "safe-array-concat": "1.1.3" } }, "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "1.3.0", "get-intrinsic": "1.3.0", "has-tostringtag": "1.0.2", "hasown": "2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "es-shim-unscopables": ["es-shim-unscopables@1.1.0", "", { "dependencies": { "hasown": "2.0.2" } }, "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw=="], + + "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "1.2.7", "is-date-object": "1.1.0", "is-symbol": "1.1.1" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="], + + "esast-util-from-estree": ["esast-util-from-estree@2.0.0", "", { "dependencies": { "@types/estree-jsx": "1.0.5", "devlop": "1.1.0", "estree-util-visit": "2.0.0", "unist-util-position-from-estree": "2.0.0" } }, "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ=="], + + "esast-util-from-js": ["esast-util-from-js@2.0.1", "", { "dependencies": { "@types/estree-jsx": "1.0.5", "acorn": "8.14.1", "esast-util-from-estree": "2.0.0", "vfile-message": "4.0.2" } }, "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw=="], + + "esbuild": ["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" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "escodegen": ["escodegen@1.14.3", "", { "dependencies": { "esprima": "4.0.1", "estraverse": "4.3.0", "esutils": "2.0.3", "optionator": "0.8.3" }, "optionalDependencies": { "source-map": "0.6.1" }, "bin": { "esgenerate": "bin/esgenerate.js", "escodegen": "bin/escodegen.js" } }, "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw=="], + + "eslint": ["eslint@9.39.5", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.6", "@eslint/js": "9.39.5", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw=="], + + "eslint-config-next": ["eslint-config-next@15.2.4", "", { "dependencies": { "@next/eslint-plugin-next": "15.2.4", "@rushstack/eslint-patch": "1.11.0", "@typescript-eslint/eslint-plugin": "8.29.1", "@typescript-eslint/parser": "5.62.0", "eslint-import-resolver-node": "0.3.9", "eslint-import-resolver-typescript": "3.10.0", "eslint-plugin-import": "2.31.0", "eslint-plugin-jsx-a11y": "6.10.2", "eslint-plugin-react": "7.37.5", "eslint-plugin-react-hooks": "5.2.0" }, "peerDependencies": { "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", "typescript": ">=3.3.1" }, "optionalPeers": ["typescript"] }, "sha512-v4gYjd4eYIme8qzaJItpR5MMBXJ0/YV07u7eb50kEnlEmX7yhOjdUdzz70v4fiINYRjLf8X8TbogF0k7wlz6sA=="], + + "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.9", "", { "dependencies": { "debug": "3.2.7", "is-core-module": "2.16.1", "resolve": "1.22.10" } }, "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g=="], + + "eslint-import-resolver-typescript": ["eslint-import-resolver-typescript@3.10.0", "", { "dependencies": { "@nolyfill/is-core-module": "1.0.39", "debug": "4.4.3", "get-tsconfig": "4.10.0", "is-bun-module": "2.0.0", "stable-hash": "0.0.5", "tinyglobby": "0.2.15", "unrs-resolver": "1.4.1" }, "peerDependencies": { "eslint": "*", "eslint-plugin-import": "*", "eslint-plugin-import-x": "*" }, "optionalPeers": ["eslint-plugin-import", "eslint-plugin-import-x"] }, "sha512-aV3/dVsT0/H9BtpNwbaqvl+0xGMRGzncLyhm793NFGvbwGGvzyAykqWZ8oZlZuGwuHkwJjhWJkG1cM3ynvd2pQ=="], + + "eslint-module-utils": ["eslint-module-utils@2.12.0", "", { "dependencies": { "debug": "3.2.7" }, "peerDependencies": { "@typescript-eslint/parser": "*", "eslint": "*", "eslint-import-resolver-node": "*", "eslint-import-resolver-typescript": "*", "eslint-import-resolver-webpack": "*" }, "optionalPeers": ["@typescript-eslint/parser", "eslint", "eslint-import-resolver-node", "eslint-import-resolver-typescript", "eslint-import-resolver-webpack"] }, "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg=="], + + "eslint-plugin-import": ["eslint-plugin-import@2.31.0", "", { "dependencies": { "@rtsao/scc": "1.1.0", "array-includes": "3.1.8", "array.prototype.findlastindex": "1.2.6", "array.prototype.flat": "1.3.3", "array.prototype.flatmap": "1.3.3", "debug": "3.2.7", "doctrine": "2.1.0", "eslint-import-resolver-node": "0.3.9", "eslint-module-utils": "2.12.0", "hasown": "2.0.2", "is-core-module": "2.16.1", "is-glob": "4.0.3", "minimatch": "3.1.2", "object.fromentries": "2.0.8", "object.groupby": "1.0.3", "object.values": "1.2.1", "semver": "6.3.1", "string.prototype.trimend": "1.0.9", "tsconfig-paths": "3.15.0" }, "peerDependencies": { "@typescript-eslint/parser": "*", "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" }, "optionalPeers": ["@typescript-eslint/parser"] }, "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A=="], + + "eslint-plugin-jsx-a11y": ["eslint-plugin-jsx-a11y@6.10.2", "", { "dependencies": { "aria-query": "5.3.2", "array-includes": "3.1.8", "array.prototype.flatmap": "1.3.3", "ast-types-flow": "0.0.8", "axe-core": "4.10.3", "axobject-query": "4.1.0", "damerau-levenshtein": "1.0.8", "emoji-regex": "9.2.2", "hasown": "2.0.2", "jsx-ast-utils": "3.3.5", "language-tags": "1.0.9", "minimatch": "3.1.2", "object.fromentries": "2.0.8", "safe-regex-test": "1.1.0", "string.prototype.includes": "2.0.1" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q=="], + + "eslint-plugin-react": ["eslint-plugin-react@7.37.5", "", { "dependencies": { "array-includes": "3.1.8", "array.prototype.findlast": "1.2.5", "array.prototype.flatmap": "1.3.3", "array.prototype.tosorted": "1.1.4", "doctrine": "2.1.0", "es-iterator-helpers": "1.2.1", "estraverse": "5.3.0", "hasown": "2.0.2", "jsx-ast-utils": "3.3.5", "minimatch": "3.1.2", "object.entries": "1.1.9", "object.fromentries": "2.0.8", "object.values": "1.2.1", "prop-types": "15.8.1", "resolve": "2.0.0-next.5", "semver": "6.3.1", "string.prototype.matchall": "4.0.12", "string.prototype.repeat": "1.0.0" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA=="], + + "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@5.2.0", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="], + + "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], + + "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + + "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "5.3.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], + + "esrap": ["esrap@2.2.4", "", { "dependencies": { "@jridgewell/sourcemap-codec": "1.5.0", "@typescript-eslint/types": "8.29.1" } }, "sha512-suICpxAmZ9A8bzJjEl/+rLJiDKC0X4gYWUxT6URAWBLvlXmtbZd5ySMu/N2ZGEtMCAmflUDPSehrP9BQcsGcSg=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "5.3.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "estree-util-attach-comments": ["estree-util-attach-comments@3.0.0", "", { "dependencies": { "@types/estree": "1.0.7" } }, "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw=="], + + "estree-util-build-jsx": ["estree-util-build-jsx@3.0.1", "", { "dependencies": { "@types/estree-jsx": "1.0.5", "devlop": "1.1.0", "estree-util-is-identifier-name": "3.0.0", "estree-walker": "3.0.3" } }, "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ=="], + + "estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="], + + "estree-util-scope": ["estree-util-scope@1.0.0", "", { "dependencies": { "@types/estree": "1.0.7", "devlop": "1.1.0" } }, "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ=="], + + "estree-util-to-js": ["estree-util-to-js@2.0.0", "", { "dependencies": { "@types/estree-jsx": "1.0.5", "astring": "1.9.0", "source-map": "0.7.4" } }, "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg=="], + + "estree-util-value-to-estree": ["estree-util-value-to-estree@3.5.0", "", { "dependencies": { "@types/estree": "1.0.7" } }, "sha512-aMV56R27Gv3QmfmF1MY12GWkGzzeAezAX+UplqHVASfjc9wNzI/X6hC0S9oxq61WT4aQesLGslWP9tKk6ghRZQ=="], + + "estree-util-visit": ["estree-util-visit@2.0.0", "", { "dependencies": { "@types/estree-jsx": "1.0.5", "@types/unist": "3.0.3" } }, "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww=="], + + "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "1.0.7" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + + "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "3.1.0" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], + + "eventsource-parser": ["eventsource-parser@3.1.0", "", {}, "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg=="], + + "exec-sh": ["exec-sh@0.3.6", "", {}, "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w=="], + + "execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "7.0.6", "get-stream": "8.0.1", "human-signals": "5.0.0", "is-stream": "3.0.0", "merge-stream": "2.0.0", "npm-run-path": "5.3.0", "onetime": "6.0.0", "signal-exit": "4.1.0", "strip-final-newline": "3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="], + + "exit": ["exit@0.1.2", "", {}, "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ=="], + + "expand-brackets": ["expand-brackets@2.1.4", "", { "dependencies": { "debug": "2.6.9", "define-property": "0.2.5", "extend-shallow": "2.0.1", "posix-character-classes": "0.1.1", "regex-not": "1.0.2", "snapdragon": "0.8.2", "to-regex": "3.0.2" } }, "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA=="], + + "expect": ["expect@24.9.0", "", { "dependencies": { "@jest/types": "24.9.0", "ansi-styles": "3.2.1", "jest-get-type": "24.9.0", "jest-matcher-utils": "24.9.0", "jest-message-util": "24.9.0", "jest-regex-util": "24.9.0" } }, "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q=="], + + "express": ["express@4.21.2", "", { "dependencies": { "accepts": "1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "1.0.5", "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "2.0.0", "escape-html": "1.0.3", "etag": "1.8.1", "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", "methods": "1.1.2", "on-finished": "2.4.1", "parseurl": "1.3.3", "path-to-regexp": "0.1.12", "proxy-addr": "2.0.7", "qs": "6.13.0", "range-parser": "1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "1.6.18", "utils-merge": "1.0.1", "vary": "1.1.2" } }, "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA=="], + + "express-fileupload": ["express-fileupload@1.5.2", "", { "dependencies": { "busboy": "1.6.0" } }, "sha512-wxUJn2vTHvj/kZCVmc5/bJO15C7aSMyHeuXYY3geKpeKibaAoQGcEv5+sM6nHS2T7VF+QHS4hTWPiY2mKofEdg=="], + + "express-rate-limit": ["express-rate-limit@7.5.1", "", { "peerDependencies": { "express": ">= 4.11" } }, "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw=="], + + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], + + "extend-shallow": ["extend-shallow@3.0.2", "", { "dependencies": { "assign-symbols": "1.0.0", "is-extendable": "1.0.1" } }, "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q=="], + + "extendable-error": ["extendable-error@0.1.7", "", {}, "sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg=="], + + "external-editor": ["external-editor@3.1.0", "", { "dependencies": { "chardet": "0.7.0", "iconv-lite": "0.4.24", "tmp": "0.0.33" } }, "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew=="], + + "extglob": ["extglob@2.0.4", "", { "dependencies": { "array-unique": "0.3.2", "define-property": "1.0.0", "expand-brackets": "2.1.4", "extend-shallow": "2.0.1", "fragment-cache": "0.2.1", "regex-not": "1.0.2", "snapdragon": "0.8.2", "to-regex": "3.0.2" } }, "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw=="], + + "extsprintf": ["extsprintf@1.3.0", "", {}, "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-glob": ["fast-glob@3.3.1", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "@nodelib/fs.walk": "1.2.8", "glob-parent": "5.1.2", "merge2": "1.4.1", "micromatch": "4.0.8" } }, "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg=="], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + + "fast-sha256": ["fast-sha256@1.3.0", "", {}, "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ=="], + + "fast-uri": ["fast-uri@3.1.8", "", {}, "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg=="], + + "fast-xml-builder": ["fast-xml-builder@1.1.7", "", { "dependencies": { "path-expression-matcher": "^1.1.3" } }, "sha512-Yh7/7rQuMXICNr0oMYDR2yHP6oUvmQsTToFeOWj/kIDhAwQ+c4Ol/lbcwOmEM5OHYQmh6S6EQSQ1sljCKP36bQ=="], + + "fast-xml-parser": ["fast-xml-parser@5.7.0", "", { "dependencies": { "@nodable/entities": "^2.1.0", "fast-xml-builder": "^1.1.5", "path-expression-matcher": "^1.5.0", "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-MTcrUoRQ1GSQ9iG3QJzBGquYYYeA7piZaJoIWbPFGbRn6Jj6z7xgoAyi4DrZX4y2ZIQQBF59gc/zmvvejjgoFQ=="], + + "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "1.1.0" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], + + "fb-watchman": ["fb-watchman@2.0.2", "", { "dependencies": { "bser": "2.1.1" } }, "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "fflate": ["fflate@0.4.9", "", {}, "sha512-zdxgIEddhfsyCaWpJ2SdXEP8ZMrKJ6+5jl4OupODcywU0IhRk6gdXuVGcPICyfx2H97hVK7xmJtRLPjkxAX8Vw=="], + + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "4.0.1" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], + + "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "finalhandler": ["finalhandler@1.3.1", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "2.0.0", "escape-html": "1.0.3", "on-finished": "2.4.1", "parseurl": "1.3.3", "statuses": "2.0.1", "unpipe": "1.0.0" } }, "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ=="], + + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "6.0.0", "path-exists": "4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "3.3.3", "keyv": "4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], + + "flatted": ["flatted@3.4.2", "", {}, "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA=="], + + "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], + + "for-in": ["for-in@1.0.2", "", {}, "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ=="], + + "foreach": ["foreach@2.0.6", "", {}, "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg=="], + + "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "7.0.6", "signal-exit": "4.1.0" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], + + "forever-agent": ["forever-agent@0.6.1", "", {}, "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw=="], + + "form-data": ["form-data@2.5.6", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.4", "mime-types": "^2.1.35", "safe-buffer": "^5.2.1" } }, "sha512-Ogz/E85h9tlfJzpI6TuFpGcHZFhLrb9Gw8wq9v40CxSCPnv7ahKr6Xgtkn0KYCDQJ8DNn5VoMO8EXr9V5PadyA=="], + + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + + "fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="], + + "fragment-cache": ["fragment-cache@0.2.1", "", { "dependencies": { "map-cache": "0.2.2" } }, "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA=="], + + "framer-motion": ["framer-motion@12.38.0", "", { "dependencies": { "motion-dom": "12.38.0", "motion-utils": "12.36.0", "tslib": "2.8.1" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g=="], + + "fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], + + "fs-extra": ["fs-extra@7.0.1", "", { "dependencies": { "graceful-fs": "4.2.11", "jsonfile": "4.0.0", "universalify": "0.1.2" } }, "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw=="], + + "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "fumadocs-core": ["fumadocs-core@16.7.1", "", { "dependencies": { "@formatjs/intl-localematcher": "0.8.2", "@orama/orama": "3.1.18", "@shikijs/rehype": "4.0.2", "@shikijs/transformers": "4.0.2", "estree-util-value-to-estree": "3.5.0", "github-slugger": "2.0.0", "hast-util-to-estree": "3.1.3", "hast-util-to-jsx-runtime": "2.3.6", "image-size": "2.0.2", "mdast-util-mdx": "3.0.0", "mdast-util-to-markdown": "2.1.2", "negotiator": "1.0.0", "npm-to-yarn": "3.0.1", "path-to-regexp": "8.3.0", "remark": "15.0.1", "remark-gfm": "4.0.1", "remark-rehype": "11.1.2", "scroll-into-view-if-needed": "3.1.0", "shiki": "4.0.2", "tinyglobby": "0.2.15", "unified": "11.0.5", "unist-util-visit": "5.1.0", "vfile": "6.0.3" }, "peerDependencies": { "@mdx-js/mdx": "*", "@mixedbread/sdk": "^0.46.0", "@orama/core": "1.x.x", "@oramacloud/client": "2.x.x", "@tanstack/react-router": "1.x.x", "@types/estree-jsx": "*", "@types/hast": "*", "@types/mdast": "*", "@types/react": "*", "algoliasearch": "5.x.x", "flexsearch": "*", "lucide-react": "*", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "react-router": "7.x.x", "waku": "^0.26.0 || ^0.27.0 || ^1.0.0", "zod": "4.x.x" }, "optionalPeers": ["@mdx-js/mdx", "@mixedbread/sdk", "@orama/core", "@oramacloud/client", "@tanstack/react-router", "@types/estree-jsx", "@types/hast", "@types/mdast", "@types/react", "algoliasearch", "flexsearch", "lucide-react", "next", "react", "react-dom", "react-router", "waku", "zod"] }, "sha512-I92GG4hHsa1SX/U41fzhi9VaLdCDZZVRk7wqjNfdU24lzC1Ty2fk0paqprMWD2kiEBL9+SRy903E2UM6maqq9w=="], + + "fumadocs-mdx": ["fumadocs-mdx@14.2.10", "", { "dependencies": { "@mdx-js/mdx": "3.1.1", "@standard-schema/spec": "1.1.0", "chokidar": "5.0.0", "esbuild": "0.27.4", "estree-util-value-to-estree": "3.5.0", "js-yaml": "4.1.1", "mdast-util-mdx": "3.0.0", "mdast-util-to-markdown": "2.1.2", "picocolors": "1.1.1", "picomatch": "4.0.3", "tinyexec": "1.0.4", "tinyglobby": "0.2.15", "unified": "11.0.5", "unist-util-remove-position": "5.0.0", "unist-util-visit": "5.1.0", "vfile": "6.0.3", "zod": "4.3.6" }, "peerDependencies": { "@fumadocs/mdx-remote": "^1.4.0", "@types/mdast": "*", "@types/mdx": "*", "@types/react": "*", "fumadocs-core": "^15.0.0 || ^16.0.0", "mdast-util-directive": "*", "next": "^15.3.0 || ^16.0.0", "react": "*", "vite": "6.x.x || 7.x.x || 8.x.x" }, "optionalPeers": ["@fumadocs/mdx-remote", "@types/mdast", "@types/mdx", "@types/react", "mdast-util-directive", "next", "react", "vite"], "bin": { "fumadocs-mdx": "dist/bin.js" } }, "sha512-0gITZiJb92c7xJwSMdcGBEY2+pFcRvklSNwxIAMTy4gjnuLZANjaXKw+qJ6E5+s9dO0IGlimHv5zyMYLjReg0w=="], + + "fumadocs-openapi": ["fumadocs-openapi@10.4.1", "", { "dependencies": { "@fastify/deepmerge": "3.2.1", "@fumari/json-schema-ts": "0.0.2", "@fumari/stf": "1.0.3", "@radix-ui/react-accordion": "1.2.12", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-select": "2.2.6", "@radix-ui/react-slot": "1.2.4", "@scalar/json-magic": "0.12.4", "@scalar/openapi-upgrader": "0.2.2", "ajv": "8.18.0", "class-variance-authority": "0.7.1", "dereference-json-schema": "0.2.2", "github-slugger": "2.0.0", "hast-util-to-jsx-runtime": "2.3.6", "js-yaml": "4.1.1", "lucide-react": "0.577.0", "next-themes": "0.4.6", "openapi-sampler": "1.7.2", "react-hook-form": "7.71.2", "remark": "15.0.1", "remark-rehype": "11.1.2", "tailwind-merge": "3.5.0", "xml-js": "1.6.11" }, "peerDependencies": { "@scalar/api-client-react": "*", "@types/react": "*", "fumadocs-core": "^16.7.0", "fumadocs-ui": "^16.7.0", "json-schema-typed": "*", "react": "^19.2.0", "react-dom": "^19.2.0", "shiki": "*" }, "optionalPeers": ["@scalar/api-client-react", "@types/react", "json-schema-typed", "shiki"] }, "sha512-iMEDyANcikDePUFlBE5sffjTbrUI5LELuIla+6kTZJYEuoQMLUK+plOnXq1iacPBMeCS7cSFadvE/Vp/h7oA+g=="], + + "fumadocs-ui": ["fumadocs-ui@16.7.1", "", { "dependencies": { "@fumadocs/tailwind": "0.0.3", "@radix-ui/react-accordion": "1.2.12", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-navigation-menu": "1.2.14", "@radix-ui/react-popover": "1.1.15", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-scroll-area": "1.2.10", "@radix-ui/react-slot": "1.2.4", "@radix-ui/react-tabs": "1.1.13", "class-variance-authority": "0.7.1", "lucide-react": "0.577.0", "motion": "12.38.0", "next-themes": "0.4.6", "react-medium-image-zoom": "5.4.1", "react-remove-scroll": "2.7.2", "rehype-raw": "7.0.0", "scroll-into-view-if-needed": "3.1.0", "tailwind-merge": "3.5.0", "unist-util-visit": "5.1.0" }, "peerDependencies": { "@takumi-rs/image-response": "*", "@types/mdx": "*", "@types/react": "*", "fumadocs-core": "16.7.1", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "shiki": "*" }, "optionalPeers": ["@takumi-rs/image-response", "@types/mdx", "@types/react", "next", "shiki"] }, "sha512-U/tthikvmpRv79lQ4G1O9VbmHqXectl/NHS7qpqAV4+csNAL0IBuCdGNU3PseiNqQdBSPxu3KIbb/Kz5xsbhtw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "define-properties": "1.2.1", "functions-have-names": "1.2.3", "hasown": "2.0.2", "is-callable": "1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="], + + "functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="], + + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + + "get-east-asian-width": ["get-east-asian-width@1.3.0", "", {}, "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "1.0.2", "es-define-property": "1.0.1", "es-errors": "1.3.0", "es-object-atoms": "1.1.1", "function-bind": "1.1.2", "get-proto": "1.0.1", "gopd": "1.2.0", "has-symbols": "1.1.0", "hasown": "2.0.2", "math-intrinsics": "1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "1.0.1", "es-object-atoms": "1.1.1" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "get-stream": ["get-stream@8.0.1", "", {}, "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="], + + "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "1.0.4", "es-errors": "1.3.0", "get-intrinsic": "1.3.0" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="], + + "get-tsconfig": ["get-tsconfig@4.10.0", "", { "dependencies": { "resolve-pkg-maps": "1.0.0" } }, "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A=="], + + "get-value": ["get-value@2.0.6", "", {}, "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA=="], + + "getpass": ["getpass@0.1.7", "", { "dependencies": { "assert-plus": "1.0.0" } }, "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng=="], + + "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], + + "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "1.0.0", "inflight": "1.0.6", "inherits": "2.0.4", "minimatch": "3.1.2", "once": "1.4.0", "path-is-absolute": "1.0.1" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "globals": ["globals@16.0.0", "", {}, "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A=="], + + "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "1.2.1", "gopd": "1.2.0" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], + + "globby": ["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" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], + + "growly": ["growly@1.3.0", "", {}, "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw=="], + + "har-schema": ["har-schema@2.0.0", "", {}, "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q=="], + + "har-validator": ["har-validator@5.1.5", "", { "dependencies": { "ajv": "6.12.6", "har-schema": "2.0.0" } }, "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w=="], + + "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], + + "has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], + + "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "1.0.1" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], + + "has-proto": ["has-proto@1.2.0", "", { "dependencies": { "dunder-proto": "1.0.1" } }, "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "1.1.0" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "has-value": ["has-value@1.0.0", "", { "dependencies": { "get-value": "2.0.6", "has-values": "1.0.0", "isobject": "3.0.1" } }, "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw=="], + + "has-values": ["has-values@1.0.0", "", { "dependencies": { "is-number": "3.0.0", "kind-of": "4.0.0" } }, "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "hast-util-from-parse5": ["hast-util-from-parse5@8.0.3", "", { "dependencies": { "@types/hast": "3.0.4", "@types/unist": "3.0.3", "devlop": "1.1.0", "hastscript": "9.0.1", "property-information": "7.0.0", "vfile": "6.0.3", "vfile-location": "5.0.3", "web-namespaces": "2.0.1" } }, "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg=="], + + "hast-util-parse-selector": ["hast-util-parse-selector@4.0.0", "", { "dependencies": { "@types/hast": "3.0.4" } }, "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A=="], + + "hast-util-raw": ["hast-util-raw@9.1.0", "", { "dependencies": { "@types/hast": "3.0.4", "@types/unist": "3.0.3", "@ungap/structured-clone": "1.3.0", "hast-util-from-parse5": "8.0.3", "hast-util-to-parse5": "8.0.1", "html-void-elements": "3.0.0", "mdast-util-to-hast": "13.2.0", "parse5": "7.3.0", "unist-util-position": "5.0.0", "unist-util-visit": "5.1.0", "vfile": "6.0.3", "web-namespaces": "2.0.1", "zwitch": "2.0.4" } }, "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw=="], + + "hast-util-to-estree": ["hast-util-to-estree@3.1.3", "", { "dependencies": { "@types/estree": "1.0.7", "@types/estree-jsx": "1.0.5", "@types/hast": "3.0.4", "comma-separated-tokens": "2.0.3", "devlop": "1.1.0", "estree-util-attach-comments": "3.0.0", "estree-util-is-identifier-name": "3.0.0", "hast-util-whitespace": "3.0.0", "mdast-util-mdx-expression": "2.0.1", "mdast-util-mdx-jsx": "3.2.0", "mdast-util-mdxjs-esm": "2.0.1", "property-information": "7.0.0", "space-separated-tokens": "2.0.2", "style-to-js": "1.1.16", "unist-util-position": "5.0.0", "zwitch": "2.0.4" } }, "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w=="], + + "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "3.0.4", "@types/unist": "3.0.3", "ccount": "2.0.1", "comma-separated-tokens": "2.0.3", "hast-util-whitespace": "3.0.0", "html-void-elements": "3.0.0", "mdast-util-to-hast": "13.2.0", "property-information": "7.0.0", "space-separated-tokens": "2.0.2", "stringify-entities": "4.0.4", "zwitch": "2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], + + "hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "1.0.7", "@types/hast": "3.0.4", "@types/unist": "3.0.3", "comma-separated-tokens": "2.0.3", "devlop": "1.1.0", "estree-util-is-identifier-name": "3.0.0", "hast-util-whitespace": "3.0.0", "mdast-util-mdx-expression": "2.0.1", "mdast-util-mdx-jsx": "3.2.0", "mdast-util-mdxjs-esm": "2.0.1", "property-information": "7.0.0", "space-separated-tokens": "2.0.2", "style-to-js": "1.1.16", "unist-util-position": "5.0.0", "vfile-message": "4.0.2" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="], + + "hast-util-to-parse5": ["hast-util-to-parse5@8.0.1", "", { "dependencies": { "@types/hast": "3.0.4", "comma-separated-tokens": "2.0.3", "devlop": "1.1.0", "property-information": "7.0.0", "space-separated-tokens": "2.0.2", "web-namespaces": "2.0.1", "zwitch": "2.0.4" } }, "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA=="], + + "hast-util-to-string": ["hast-util-to-string@3.0.1", "", { "dependencies": { "@types/hast": "3.0.4" } }, "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A=="], + + "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "3.0.4" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], + + "hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "3.0.4", "comma-separated-tokens": "2.0.3", "hast-util-parse-selector": "4.0.0", "property-information": "7.0.0", "space-separated-tokens": "2.0.2" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], + + "hono": ["hono@4.13.7", "", {}, "sha512-c8/gF9ac8Y78/agExVocyLevgR+JlpNB444Py0FSX8pJoPdYUfUzRcXtYEYGwt6l19qIlVZPN5Mfsw9jFShmQQ=="], + + "hosted-git-info": ["hosted-git-info@2.8.9", "", {}, "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="], + + "html-encoding-sniffer": ["html-encoding-sniffer@1.0.2", "", { "dependencies": { "whatwg-encoding": "1.0.5" } }, "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw=="], + + "html-escaper": ["html-escaper@2.0.2", "", {}, "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="], + + "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], + + "http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], + + "http-signature": ["http-signature@1.2.0", "", { "dependencies": { "assert-plus": "1.0.0", "jsprim": "1.4.2", "sshpk": "1.18.0" } }, "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ=="], + + "human-id": ["human-id@4.1.1", "", { "bin": { "human-id": "dist/cli.js" } }, "sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg=="], + + "human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="], + + "husky": ["husky@9.1.7", "", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="], + + "iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": "2.1.2" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], + + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "image-size": ["image-size@2.0.3", "", { "bin": { "image-size": "bin/image-size.js" } }, "sha512-9BJAtVCBth+76NwqnEfWr3PiRnYhbquk6K/USuQ1GQajz5NgLzO7tNsnoNjHfO7LuftgkBf+CCH6j28hjMbIYQ=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "1.0.1", "resolve-from": "4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "import-local": ["import-local@2.0.0", "", { "dependencies": { "pkg-dir": "3.0.0", "resolve-cwd": "2.0.0" }, "bin": { "import-local-fixture": "fixtures/cli.js" } }, "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "1.4.0", "wrappy": "1.0.2" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "inline-style-parser": ["inline-style-parser@0.2.4", "", {}, "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q=="], + + "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "1.3.0", "hasown": "2.0.2", "side-channel": "1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], + + "invariant": ["invariant@2.2.4", "", { "dependencies": { "loose-envify": "1.4.0" } }, "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA=="], + + "ioredis": ["ioredis@5.8.2", "", { "dependencies": { "@ioredis/commands": "1.4.0", "cluster-key-slot": "1.1.2", "debug": "4.4.0", "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" } }, "sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q=="], + + "ip-address": ["ip-address@10.7.1", "", {}, "sha512-4OUAqU9Z1i3vCnS05hzGiFnEMDpQ+62pAD/MVQOp83fYyNC8GleCqaS0QikQBmcWCrKFiUs/B8ztRRiYOAXuCA=="], + + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + + "is-accessor-descriptor": ["is-accessor-descriptor@1.0.1", "", { "dependencies": { "hasown": "2.0.2" } }, "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA=="], + + "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], + + "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "2.0.1", "is-decimal": "2.0.1" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="], + + "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "get-intrinsic": "1.3.0" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="], + + "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], + + "is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "1.0.0", "call-bound": "1.0.4", "get-proto": "1.0.1", "has-tostringtag": "1.0.2", "safe-regex-test": "1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="], + + "is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "1.1.0" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="], + + "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "2.3.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], + + "is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "1.0.4", "has-tostringtag": "1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="], + + "is-buffer": ["is-buffer@1.1.6", "", {}, "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="], + + "is-bun-module": ["is-bun-module@2.0.0", "", { "dependencies": { "semver": "7.7.3" } }, "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ=="], + + "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], + + "is-ci": ["is-ci@2.0.0", "", { "dependencies": { "ci-info": "2.0.0" }, "bin": { "is-ci": "bin.js" } }, "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w=="], + + "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], + + "is-data-descriptor": ["is-data-descriptor@1.0.1", "", { "dependencies": { "hasown": "2.0.2" } }, "sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw=="], + + "is-data-view": ["is-data-view@1.0.2", "", { "dependencies": { "call-bound": "1.0.4", "get-intrinsic": "1.3.0", "is-typed-array": "1.1.15" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="], + + "is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "1.0.4", "has-tostringtag": "1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="], + + "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="], + + "is-descriptor": ["is-descriptor@1.0.3", "", { "dependencies": { "is-accessor-descriptor": "1.0.1", "is-data-descriptor": "1.0.1" } }, "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw=="], + + "is-extendable": ["is-extendable@1.0.1", "", { "dependencies": { "is-plain-object": "2.0.4" } }, "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "1.0.4" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], + + "is-generator-fn": ["is-generator-fn@2.1.0", "", {}, "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ=="], + + "is-generator-function": ["is-generator-function@1.1.0", "", { "dependencies": { "call-bound": "1.0.4", "get-proto": "1.0.1", "has-tostringtag": "1.0.2", "safe-regex-test": "1.1.0" } }, "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="], + + "is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "1.0.4", "has-tostringtag": "1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="], + + "is-path-inside": ["is-path-inside@3.0.3", "", {}, "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="], + + "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], + + "is-plain-object": ["is-plain-object@2.0.4", "", { "dependencies": { "isobject": "3.0.1" } }, "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og=="], + + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], + + "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "1.0.4", "gopd": "1.2.0", "has-tostringtag": "1.0.2", "hasown": "2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], + + "is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="], + + "is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "1.0.4" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="], + + "is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + + "is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "1.0.4", "has-tostringtag": "1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="], + + "is-subdir": ["is-subdir@1.2.0", "", { "dependencies": { "better-path-resolve": "1.0.0" } }, "sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw=="], + + "is-symbol": ["is-symbol@1.1.1", "", { "dependencies": { "call-bound": "1.0.4", "has-symbols": "1.1.0", "safe-regex-test": "1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="], + + "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "1.1.19" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], + + "is-typedarray": ["is-typedarray@1.0.0", "", {}, "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="], + + "is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="], + + "is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "1.0.4" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="], + + "is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "1.0.4", "get-intrinsic": "1.3.0" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="], + + "is-windows": ["is-windows@1.0.2", "", {}, "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="], + + "is-wsl": ["is-wsl@1.1.0", "", {}, "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw=="], + + "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "isobject": ["isobject@3.0.1", "", {}, "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="], + + "isstream": ["isstream@0.1.2", "", {}, "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g=="], + + "istanbul-lib-coverage": ["istanbul-lib-coverage@2.0.5", "", {}, "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA=="], + + "istanbul-lib-instrument": ["istanbul-lib-instrument@3.3.0", "", { "dependencies": { "@babel/generator": "7.27.0", "@babel/parser": "7.27.0", "@babel/template": "7.27.0", "@babel/traverse": "7.27.0", "@babel/types": "7.27.0", "istanbul-lib-coverage": "2.0.5", "semver": "6.3.1" } }, "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA=="], + + "istanbul-lib-report": ["istanbul-lib-report@2.0.8", "", { "dependencies": { "istanbul-lib-coverage": "2.0.5", "make-dir": "2.1.0", "supports-color": "6.1.0" } }, "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ=="], + + "istanbul-lib-source-maps": ["istanbul-lib-source-maps@3.0.6", "", { "dependencies": { "debug": "4.4.3", "istanbul-lib-coverage": "2.0.5", "make-dir": "2.1.0", "rimraf": "2.7.1", "source-map": "0.6.1" } }, "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw=="], + + "istanbul-reports": ["istanbul-reports@2.2.7", "", { "dependencies": { "html-escaper": "2.0.2" } }, "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg=="], + + "iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "1.1.4", "es-object-atoms": "1.1.1", "get-intrinsic": "1.3.0", "get-proto": "1.0.1", "has-symbols": "1.1.0", "set-function-name": "2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="], + + "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], + + "jest": ["jest@24.9.0", "", { "dependencies": { "import-local": "2.0.0", "jest-cli": "24.9.0" }, "bin": { "jest": "./bin/jest.js" } }, "sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw=="], + + "jest-changed-files": ["jest-changed-files@24.9.0", "", { "dependencies": { "@jest/types": "24.9.0", "execa": "1.0.0", "throat": "4.1.0" } }, "sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg=="], + + "jest-cli": ["jest-cli@24.9.0", "", { "dependencies": { "@jest/core": "24.9.0", "@jest/test-result": "24.9.0", "@jest/types": "24.9.0", "chalk": "2.4.2", "exit": "0.1.2", "import-local": "2.0.0", "is-ci": "2.0.0", "jest-config": "24.9.0", "jest-util": "24.9.0", "jest-validate": "24.9.0", "prompts": "2.4.2", "realpath-native": "1.1.0", "yargs": "13.3.2" }, "bin": { "jest": "./bin/jest.js" } }, "sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg=="], + + "jest-config": ["jest-config@24.9.0", "", { "dependencies": { "@babel/core": "7.26.10", "@jest/test-sequencer": "24.9.0", "@jest/types": "24.9.0", "babel-jest": "24.9.0", "chalk": "2.4.2", "glob": "7.2.3", "jest-environment-jsdom": "24.9.0", "jest-environment-node": "24.9.0", "jest-get-type": "24.9.0", "jest-jasmine2": "24.9.0", "jest-regex-util": "24.9.0", "jest-resolve": "24.9.0", "jest-util": "24.9.0", "jest-validate": "24.9.0", "micromatch": "3.1.10", "pretty-format": "24.9.0", "realpath-native": "1.1.0" } }, "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ=="], + + "jest-diff": ["jest-diff@24.9.0", "", { "dependencies": { "chalk": "2.4.2", "diff-sequences": "24.9.0", "jest-get-type": "24.9.0", "pretty-format": "24.9.0" } }, "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ=="], + + "jest-docblock": ["jest-docblock@24.9.0", "", { "dependencies": { "detect-newline": "2.1.0" } }, "sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA=="], + + "jest-each": ["jest-each@24.9.0", "", { "dependencies": { "@jest/types": "24.9.0", "chalk": "2.4.2", "jest-get-type": "24.9.0", "jest-util": "24.9.0", "pretty-format": "24.9.0" } }, "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog=="], + + "jest-environment-jsdom": ["jest-environment-jsdom@24.9.0", "", { "dependencies": { "@jest/environment": "24.9.0", "@jest/fake-timers": "24.9.0", "@jest/types": "24.9.0", "jest-mock": "24.9.0", "jest-util": "24.9.0", "jsdom": "11.12.0" } }, "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA=="], + + "jest-environment-node": ["jest-environment-node@24.9.0", "", { "dependencies": { "@jest/environment": "24.9.0", "@jest/fake-timers": "24.9.0", "@jest/types": "24.9.0", "jest-mock": "24.9.0", "jest-util": "24.9.0" } }, "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA=="], + + "jest-get-type": ["jest-get-type@24.9.0", "", {}, "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q=="], + + "jest-haste-map": ["jest-haste-map@24.9.0", "", { "dependencies": { "@jest/types": "24.9.0", "anymatch": "2.0.0", "fb-watchman": "2.0.2", "graceful-fs": "4.2.11", "invariant": "2.2.4", "jest-serializer": "24.9.0", "jest-util": "24.9.0", "jest-worker": "24.9.0", "micromatch": "3.1.10", "sane": "4.1.0", "walker": "1.0.8" }, "optionalDependencies": { "fsevents": "1.2.13" } }, "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ=="], + + "jest-jasmine2": ["jest-jasmine2@24.9.0", "", { "dependencies": { "@babel/traverse": "7.27.0", "@jest/environment": "24.9.0", "@jest/test-result": "24.9.0", "@jest/types": "24.9.0", "chalk": "2.4.2", "co": "4.6.0", "expect": "24.9.0", "is-generator-fn": "2.1.0", "jest-each": "24.9.0", "jest-matcher-utils": "24.9.0", "jest-message-util": "24.9.0", "jest-runtime": "24.9.0", "jest-snapshot": "24.9.0", "jest-util": "24.9.0", "pretty-format": "24.9.0", "throat": "4.1.0" } }, "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw=="], + + "jest-leak-detector": ["jest-leak-detector@24.9.0", "", { "dependencies": { "jest-get-type": "24.9.0", "pretty-format": "24.9.0" } }, "sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA=="], + + "jest-matcher-utils": ["jest-matcher-utils@24.9.0", "", { "dependencies": { "chalk": "2.4.2", "jest-diff": "24.9.0", "jest-get-type": "24.9.0", "pretty-format": "24.9.0" } }, "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA=="], + + "jest-message-util": ["jest-message-util@24.9.0", "", { "dependencies": { "@babel/code-frame": "7.26.2", "@jest/test-result": "24.9.0", "@jest/types": "24.9.0", "@types/stack-utils": "1.0.1", "chalk": "2.4.2", "micromatch": "3.1.10", "slash": "2.0.0", "stack-utils": "1.0.5" } }, "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw=="], + + "jest-mock": ["jest-mock@24.9.0", "", { "dependencies": { "@jest/types": "24.9.0" } }, "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w=="], + + "jest-pnp-resolver": ["jest-pnp-resolver@1.2.3", "", { "peerDependencies": { "jest-resolve": "*" }, "optionalPeers": ["jest-resolve"] }, "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w=="], + + "jest-regex-util": ["jest-regex-util@24.9.0", "", {}, "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA=="], + + "jest-resolve": ["jest-resolve@24.9.0", "", { "dependencies": { "@jest/types": "24.9.0", "browser-resolve": "1.11.3", "chalk": "2.4.2", "jest-pnp-resolver": "1.2.3", "realpath-native": "1.1.0" } }, "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ=="], + + "jest-resolve-dependencies": ["jest-resolve-dependencies@24.9.0", "", { "dependencies": { "@jest/types": "24.9.0", "jest-regex-util": "24.9.0", "jest-snapshot": "24.9.0" } }, "sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g=="], + + "jest-runner": ["jest-runner@24.9.0", "", { "dependencies": { "@jest/console": "24.9.0", "@jest/environment": "24.9.0", "@jest/test-result": "24.9.0", "@jest/types": "24.9.0", "chalk": "2.4.2", "exit": "0.1.2", "graceful-fs": "4.2.11", "jest-config": "24.9.0", "jest-docblock": "24.9.0", "jest-haste-map": "24.9.0", "jest-jasmine2": "24.9.0", "jest-leak-detector": "24.9.0", "jest-message-util": "24.9.0", "jest-resolve": "24.9.0", "jest-runtime": "24.9.0", "jest-util": "24.9.0", "jest-worker": "24.9.0", "source-map-support": "0.5.21", "throat": "4.1.0" } }, "sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg=="], + + "jest-runtime": ["jest-runtime@24.9.0", "", { "dependencies": { "@jest/console": "24.9.0", "@jest/environment": "24.9.0", "@jest/source-map": "24.9.0", "@jest/transform": "24.9.0", "@jest/types": "24.9.0", "@types/yargs": "13.0.12", "chalk": "2.4.2", "exit": "0.1.2", "glob": "7.2.3", "graceful-fs": "4.2.11", "jest-config": "24.9.0", "jest-haste-map": "24.9.0", "jest-message-util": "24.9.0", "jest-mock": "24.9.0", "jest-regex-util": "24.9.0", "jest-resolve": "24.9.0", "jest-snapshot": "24.9.0", "jest-util": "24.9.0", "jest-validate": "24.9.0", "realpath-native": "1.1.0", "slash": "2.0.0", "strip-bom": "3.0.0", "yargs": "13.3.2" }, "bin": { "jest-runtime": "./bin/jest-runtime.js" } }, "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw=="], + + "jest-serializer": ["jest-serializer@24.9.0", "", {}, "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ=="], + + "jest-snapshot": ["jest-snapshot@24.9.0", "", { "dependencies": { "@babel/types": "7.27.0", "@jest/types": "24.9.0", "chalk": "2.4.2", "expect": "24.9.0", "jest-diff": "24.9.0", "jest-get-type": "24.9.0", "jest-matcher-utils": "24.9.0", "jest-message-util": "24.9.0", "jest-resolve": "24.9.0", "mkdirp": "0.5.6", "natural-compare": "1.4.0", "pretty-format": "24.9.0", "semver": "6.3.1" } }, "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew=="], + + "jest-util": ["jest-util@24.9.0", "", { "dependencies": { "@jest/console": "24.9.0", "@jest/fake-timers": "24.9.0", "@jest/source-map": "24.9.0", "@jest/test-result": "24.9.0", "@jest/types": "24.9.0", "callsites": "3.1.0", "chalk": "2.4.2", "graceful-fs": "4.2.11", "is-ci": "2.0.0", "mkdirp": "0.5.6", "slash": "2.0.0", "source-map": "0.6.1" } }, "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg=="], + + "jest-validate": ["jest-validate@24.9.0", "", { "dependencies": { "@jest/types": "24.9.0", "camelcase": "5.3.1", "chalk": "2.4.2", "jest-get-type": "24.9.0", "leven": "3.1.0", "pretty-format": "24.9.0" } }, "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ=="], + + "jest-watcher": ["jest-watcher@24.9.0", "", { "dependencies": { "@jest/test-result": "24.9.0", "@jest/types": "24.9.0", "@types/yargs": "13.0.12", "ansi-escapes": "3.2.0", "chalk": "2.4.2", "jest-util": "24.9.0", "string-length": "2.0.0" } }, "sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw=="], + + "jest-worker": ["jest-worker@24.9.0", "", { "dependencies": { "merge-stream": "2.0.0", "supports-color": "6.1.0" } }, "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw=="], + + "jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], + + "joi": ["joi@17.13.7", "", { "dependencies": { "@hapi/hoek": "^9.3.0", "@hapi/topo": "^5.1.0", "@sideway/address": "^4.1.5", "@sideway/formula": "^3.0.1", "@sideway/pinpoint": "^2.0.0" } }, "sha512-MF80Dm5Y2veNy8QWVx9Bj3ui4mo7+VPSPsR1M+oaHXV0Gx6zGX9a2F+OZG3Blby9tOlzU9Rs5FUimlEhbKtfnQ=="], + + "joi-to-swagger": ["joi-to-swagger@6.2.0", "", { "dependencies": { "lodash": "4.17.21" }, "peerDependencies": { "joi": ">=17.1.1" } }, "sha512-gwfIr1TsbbvZWozB/sFqiD7POFcXeaLKp6QJKGFkVgdom2ie/4f75QQAanZc/Wlbnyk66e6kTZXO28i6pN3oQA=="], + + "jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="], + + "joycon": ["joycon@3.1.1", "", {}, "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw=="], + + "js-base64": ["js-base64@3.7.8", "", {}, "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "js-yaml": ["js-yaml@4.3.2", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA=="], + + "jsbn": ["jsbn@0.1.1", "", {}, "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg=="], + + "jsdom": ["jsdom@11.12.0", "", { "dependencies": { "abab": "2.0.6", "acorn": "5.7.4", "acorn-globals": "4.3.4", "array-equal": "1.0.2", "cssom": "0.3.8", "cssstyle": "1.4.0", "data-urls": "1.1.0", "domexception": "1.0.1", "escodegen": "1.14.3", "html-encoding-sniffer": "1.0.2", "left-pad": "1.3.0", "nwsapi": "2.2.20", "parse5": "4.0.0", "pn": "1.1.0", "request": "2.88.2", "request-promise-native": "1.0.9", "sax": "1.4.1", "symbol-tree": "3.2.4", "tough-cookie": "2.5.0", "w3c-hr-time": "1.0.2", "webidl-conversions": "4.0.2", "whatwg-encoding": "1.0.5", "whatwg-mimetype": "2.3.0", "whatwg-url": "6.5.0", "ws": "5.2.4", "xml-name-validator": "3.0.0" } }, "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw=="], + + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-parse-better-errors": ["json-parse-better-errors@1.0.2", "", {}, "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="], + + "json-pointer": ["json-pointer@0.6.2", "", { "dependencies": { "foreach": "2.0.6" } }, "sha512-vLWcKbOaXlO+jvRy4qNd+TI1QUPZzfJj1tpJ3vAXDych5XJf93ftpUKe5pKCrzyIIwgBJcOcCVRUfqQP25afBw=="], + + "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="], + + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + + "jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "4.2.11" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], + + "jsonwebtoken": ["jsonwebtoken@9.0.2", "", { "dependencies": { "jws": "3.2.2", "lodash.includes": "4.3.0", "lodash.isboolean": "3.0.3", "lodash.isinteger": "4.0.4", "lodash.isnumber": "3.0.3", "lodash.isplainobject": "4.0.6", "lodash.isstring": "4.0.1", "lodash.once": "4.1.1", "ms": "2.1.3", "semver": "7.7.3" } }, "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ=="], + + "jsprim": ["jsprim@1.4.2", "", { "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", "json-schema": "0.4.0", "verror": "1.10.0" } }, "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw=="], + + "jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "3.1.8", "array.prototype.flat": "1.3.3", "object.assign": "4.1.7", "object.values": "1.2.1" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="], + + "jwa": ["jwa@1.4.2", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw=="], + + "jws": ["jws@3.2.3", "", { "dependencies": { "jwa": "^1.4.2", "safe-buffer": "^5.0.1" } }, "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g=="], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + + "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], + + "kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], + + "kysely": ["kysely@0.29.6", "", {}, "sha512-hHaB8C/rfzDDtr/t8YZwxAuPJTT0zHyaPoVzcXwDYhYNAgH/4sIfVhi/XLLIY+bL/FqaIJnjATDbi8ObSELmxg=="], + + "language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="], + + "language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "0.3.23" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="], + + "left-pad": ["left-pad@1.3.0", "", {}, "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA=="], + + "leven": ["leven@3.1.0", "", {}, "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "1.2.1", "type-check": "0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "lightningcss": ["lightningcss@1.29.2", "", { "dependencies": { "detect-libc": "2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.29.2", "lightningcss-darwin-x64": "1.29.2", "lightningcss-freebsd-x64": "1.29.2", "lightningcss-linux-arm-gnueabihf": "1.29.2", "lightningcss-linux-arm64-gnu": "1.29.2", "lightningcss-linux-arm64-musl": "1.29.2", "lightningcss-linux-x64-gnu": "1.29.2", "lightningcss-linux-x64-musl": "1.29.2", "lightningcss-win32-arm64-msvc": "1.29.2", "lightningcss-win32-x64-msvc": "1.29.2" } }, "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.29.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.29.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.29.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.29.2", "", { "os": "linux", "cpu": "arm" }, "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.29.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.29.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.29.2", "", { "os": "linux", "cpu": "x64" }, "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.29.2", "", { "os": "linux", "cpu": "x64" }, "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.29.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.29.2", "", { "os": "win32", "cpu": "x64" }, "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA=="], + + "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], + + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + + "lint-staged": ["lint-staged@15.5.0", "", { "dependencies": { "chalk": "5.4.1", "commander": "13.1.0", "debug": "4.4.0", "execa": "8.0.1", "lilconfig": "3.1.3", "listr2": "8.2.5", "micromatch": "4.0.8", "pidtree": "0.6.0", "string-argv": "0.3.2", "yaml": "2.7.1" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-WyCzSbfYGhK7cU+UuDDkzUiytbfbi0ZdPy2orwtM75P3WTtQBzmG40cCxIa8Ii2+XjfxzLH6Be46tUfWS85Xfg=="], + + "listr2": ["listr2@8.2.5", "", { "dependencies": { "cli-truncate": "4.0.0", "colorette": "2.0.20", "eventemitter3": "5.0.1", "log-update": "6.1.0", "rfdc": "1.4.1", "wrap-ansi": "9.0.0" } }, "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ=="], + + "load-json-file": ["load-json-file@4.0.0", "", { "dependencies": { "graceful-fs": "4.2.11", "parse-json": "4.0.0", "pify": "3.0.0", "strip-bom": "3.0.0" } }, "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw=="], + + "load-tsconfig": ["load-tsconfig@0.2.5", "", {}, "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash": ["lodash@4.18.0", "", {}, "sha512-l1mfj2atMqndAHI3ls7XqPxEjV2J9ZkcNyHpoZA3r2T1LLwDB69jgkMWh71YKwhBbK0G2f4WSn05ahmQXVxupA=="], + + "lodash._baseiteratee": ["lodash._baseiteratee@4.7.0", "", { "dependencies": { "lodash._stringtopath": "4.8.0" } }, "sha512-nqB9M+wITz0BX/Q2xg6fQ8mLkyfF7MU7eE+MNBNjTHFKeKaZAPEzEg+E8LWxKWf1DQVflNEn9N49yAuqKh2mWQ=="], + + "lodash._basetostring": ["lodash._basetostring@4.12.0", "", {}, "sha512-SwcRIbyxnN6CFEEK4K1y+zuApvWdpQdBHM/swxP962s8HIxPO3alBH5t3m/dl+f4CMUug6sJb7Pww8d13/9WSw=="], + + "lodash._baseuniq": ["lodash._baseuniq@4.6.0", "", { "dependencies": { "lodash._createset": "4.0.3", "lodash._root": "3.0.1" } }, "sha512-Ja1YevpHZctlI5beLA7oc5KNDhGcPixFhcqSiORHNsp/1QTv7amAXzw+gu4YOvErqVlMVyIJGgtzeepCnnur0A=="], + + "lodash._createset": ["lodash._createset@4.0.3", "", {}, "sha512-GTkC6YMprrJZCYU3zcqZj+jkXkrXzq3IPBcF/fIPpNEAB4hZEtXU8zp/RwKOvZl43NUmwDbyRk3+ZTbeRdEBXA=="], + + "lodash._root": ["lodash._root@3.0.1", "", {}, "sha512-O0pWuFSK6x4EXhM1dhZ8gchNtG7JMqBtrHdoUFUWXD7dJnNSUze1GuyQr5sOs0aCvgGeI3o/OJW8f4ca7FDxmQ=="], + + "lodash._stringtopath": ["lodash._stringtopath@4.8.0", "", { "dependencies": { "lodash._basetostring": "4.12.0" } }, "sha512-SXL66C731p0xPDC5LZg4wI5H+dJo/EO4KTqOMwLYCH3+FmmfAKJEZCm6ohGpI+T1xwsDsJCfL4OnhorllvlTPQ=="], + + "lodash.defaults": ["lodash.defaults@4.2.0", "", {}, "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="], + + "lodash.includes": ["lodash.includes@4.3.0", "", {}, "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="], + + "lodash.isarguments": ["lodash.isarguments@3.1.0", "", {}, "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="], + + "lodash.isboolean": ["lodash.isboolean@3.0.3", "", {}, "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="], + + "lodash.isinteger": ["lodash.isinteger@4.0.4", "", {}, "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="], + + "lodash.isnumber": ["lodash.isnumber@3.0.3", "", {}, "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="], + + "lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="], + + "lodash.isstring": ["lodash.isstring@4.0.1", "", {}, "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "lodash.once": ["lodash.once@4.1.1", "", {}, "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="], + + "lodash.sortby": ["lodash.sortby@4.7.0", "", {}, "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA=="], + + "lodash.startcase": ["lodash.startcase@4.4.0", "", {}, "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg=="], + + "lodash.throttle": ["lodash.throttle@4.1.1", "", {}, "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="], + + "lodash.uniqby": ["lodash.uniqby@4.5.0", "", { "dependencies": { "lodash._baseiteratee": "4.7.0", "lodash._baseuniq": "4.6.0" } }, "sha512-IRt7cfTtHy6f1aRVA5n7kT8rgN3N1nH6MOWLcHfpWG2SH19E3JksLK38MktLxZDhlAjCP9jpIXkOnRXlu6oByQ=="], + + "log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "7.0.0", "cli-cursor": "5.0.0", "slice-ansi": "7.1.0", "strip-ansi": "7.1.0", "wrap-ansi": "9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="], + + "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + + "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "3.1.1" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "lucide-react": ["lucide-react@0.487.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-aKqhOQ+YmFnwq8dWgGjOuLc8V1R9/c/yOd+zDY4+ohsR2Jo05lSGc3WsstYPIzcTpeosN7LoCkLReUUITvaIvw=="], + + "make-dir": ["make-dir@2.1.0", "", { "dependencies": { "pify": "4.0.1", "semver": "5.7.2" } }, "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA=="], + + "makeerror": ["makeerror@1.0.12", "", { "dependencies": { "tmpl": "1.0.5" } }, "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg=="], + + "map-cache": ["map-cache@0.2.2", "", {}, "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg=="], + + "map-visit": ["map-visit@1.0.0", "", { "dependencies": { "object-visit": "1.0.1" } }, "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w=="], + + "markdown-extensions": ["markdown-extensions@2.0.0", "", {}, "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q=="], + + "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "4.0.4", "escape-string-regexp": "5.0.0", "unist-util-is": "6.0.0", "unist-util-visit-parents": "6.0.1" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="], + + "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.2", "", { "dependencies": { "@types/mdast": "4.0.4", "@types/unist": "3.0.3", "decode-named-character-reference": "1.1.0", "devlop": "1.1.0", "mdast-util-to-string": "4.0.0", "micromark": "4.0.2", "micromark-util-decode-numeric-character-reference": "2.0.2", "micromark-util-decode-string": "2.0.1", "micromark-util-normalize-identifier": "2.0.1", "micromark-util-symbol": "2.0.1", "micromark-util-types": "2.0.2", "unist-util-stringify-position": "4.0.0" } }, "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="], + + "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "2.0.2", "mdast-util-gfm-autolink-literal": "2.0.1", "mdast-util-gfm-footnote": "2.1.0", "mdast-util-gfm-strikethrough": "2.0.0", "mdast-util-gfm-table": "2.0.0", "mdast-util-gfm-task-list-item": "2.0.0", "mdast-util-to-markdown": "2.1.2" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="], + + "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "4.0.4", "ccount": "2.0.1", "devlop": "1.1.0", "mdast-util-find-and-replace": "3.0.2", "micromark-util-character": "2.1.1" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="], + + "mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "4.0.4", "devlop": "1.1.0", "mdast-util-from-markdown": "2.0.2", "mdast-util-to-markdown": "2.1.2", "micromark-util-normalize-identifier": "2.0.1" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="], + + "mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.0", "", { "dependencies": { "@types/mdast": "4.0.4", "mdast-util-from-markdown": "2.0.2", "mdast-util-to-markdown": "2.1.2" } }, "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg=="], + + "mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "4.0.4", "devlop": "1.1.0", "markdown-table": "3.0.4", "mdast-util-from-markdown": "2.0.2", "mdast-util-to-markdown": "2.1.2" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="], + + "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "4.0.4", "devlop": "1.1.0", "mdast-util-from-markdown": "2.0.2", "mdast-util-to-markdown": "2.1.2" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="], + + "mdast-util-mdx": ["mdast-util-mdx@3.0.0", "", { "dependencies": { "mdast-util-from-markdown": "2.0.2", "mdast-util-mdx-expression": "2.0.1", "mdast-util-mdx-jsx": "3.2.0", "mdast-util-mdxjs-esm": "2.0.1", "mdast-util-to-markdown": "2.1.2" } }, "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w=="], + + "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "1.0.5", "@types/hast": "3.0.4", "@types/mdast": "4.0.4", "devlop": "1.1.0", "mdast-util-from-markdown": "2.0.2", "mdast-util-to-markdown": "2.1.2" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="], + + "mdast-util-mdx-jsx": ["mdast-util-mdx-jsx@3.2.0", "", { "dependencies": { "@types/estree-jsx": "1.0.5", "@types/hast": "3.0.4", "@types/mdast": "4.0.4", "@types/unist": "3.0.3", "ccount": "2.0.1", "devlop": "1.1.0", "mdast-util-from-markdown": "2.0.2", "mdast-util-to-markdown": "2.1.2", "parse-entities": "4.0.2", "stringify-entities": "4.0.4", "unist-util-stringify-position": "4.0.0", "vfile-message": "4.0.2" } }, "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q=="], + + "mdast-util-mdxjs-esm": ["mdast-util-mdxjs-esm@2.0.1", "", { "dependencies": { "@types/estree-jsx": "1.0.5", "@types/hast": "3.0.4", "@types/mdast": "4.0.4", "devlop": "1.1.0", "mdast-util-from-markdown": "2.0.2", "mdast-util-to-markdown": "2.1.2" } }, "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg=="], + + "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "4.0.4", "unist-util-is": "6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="], + + "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="], + + "mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "4.0.4", "@types/unist": "3.0.3", "longest-streak": "3.1.0", "mdast-util-phrasing": "4.1.0", "mdast-util-to-string": "4.0.0", "micromark-util-classify-character": "2.0.1", "micromark-util-decode-string": "2.0.1", "unist-util-visit": "5.1.0", "zwitch": "2.0.4" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="], + + "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "4.0.4" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], + + "media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], + + "medialit": ["medialit@workspace:packages/medialit"], + + "memory-pager": ["memory-pager@1.5.0", "", {}, "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="], + + "merge-descriptors": ["merge-descriptors@1.0.3", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="], + + "merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="], + + "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "4.1.12", "debug": "4.4.3", "decode-named-character-reference": "1.1.0", "devlop": "1.1.0", "micromark-core-commonmark": "2.0.3", "micromark-factory-space": "2.0.1", "micromark-util-character": "2.1.1", "micromark-util-chunked": "2.0.1", "micromark-util-combine-extensions": "2.0.1", "micromark-util-decode-numeric-character-reference": "2.0.2", "micromark-util-encode": "2.0.1", "micromark-util-normalize-identifier": "2.0.1", "micromark-util-resolve-all": "2.0.1", "micromark-util-sanitize-uri": "2.0.1", "micromark-util-subtokenize": "2.1.0", "micromark-util-symbol": "2.0.1", "micromark-util-types": "2.0.2" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="], + + "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "1.1.0", "devlop": "1.1.0", "micromark-factory-destination": "2.0.1", "micromark-factory-label": "2.0.1", "micromark-factory-space": "2.0.1", "micromark-factory-title": "2.0.1", "micromark-factory-whitespace": "2.0.1", "micromark-util-character": "2.1.1", "micromark-util-chunked": "2.0.1", "micromark-util-classify-character": "2.0.1", "micromark-util-html-tag-name": "2.0.1", "micromark-util-normalize-identifier": "2.0.1", "micromark-util-resolve-all": "2.0.1", "micromark-util-subtokenize": "2.1.0", "micromark-util-symbol": "2.0.1", "micromark-util-types": "2.0.2" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], + + "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "2.1.0", "micromark-extension-gfm-footnote": "2.1.0", "micromark-extension-gfm-strikethrough": "2.1.0", "micromark-extension-gfm-table": "2.1.1", "micromark-extension-gfm-tagfilter": "2.0.0", "micromark-extension-gfm-task-list-item": "2.1.0", "micromark-util-combine-extensions": "2.0.1", "micromark-util-types": "2.0.2" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="], + + "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "2.1.1", "micromark-util-sanitize-uri": "2.0.1", "micromark-util-symbol": "2.0.1", "micromark-util-types": "2.0.2" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], + + "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "1.1.0", "micromark-core-commonmark": "2.0.3", "micromark-factory-space": "2.0.1", "micromark-util-character": "2.1.1", "micromark-util-normalize-identifier": "2.0.1", "micromark-util-sanitize-uri": "2.0.1", "micromark-util-symbol": "2.0.1", "micromark-util-types": "2.0.2" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="], + + "micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "1.1.0", "micromark-util-chunked": "2.0.1", "micromark-util-classify-character": "2.0.1", "micromark-util-resolve-all": "2.0.1", "micromark-util-symbol": "2.0.1", "micromark-util-types": "2.0.2" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="], + + "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "1.1.0", "micromark-factory-space": "2.0.1", "micromark-util-character": "2.1.1", "micromark-util-symbol": "2.0.1", "micromark-util-types": "2.0.2" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="], + + "micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "2.0.2" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="], + + "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "1.1.0", "micromark-factory-space": "2.0.1", "micromark-util-character": "2.1.1", "micromark-util-symbol": "2.0.1", "micromark-util-types": "2.0.2" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="], + + "micromark-extension-mdx-expression": ["micromark-extension-mdx-expression@3.0.1", "", { "dependencies": { "@types/estree": "1.0.7", "devlop": "1.1.0", "micromark-factory-mdx-expression": "2.0.3", "micromark-factory-space": "2.0.1", "micromark-util-character": "2.1.1", "micromark-util-events-to-acorn": "2.0.3", "micromark-util-symbol": "2.0.1", "micromark-util-types": "2.0.2" } }, "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q=="], + + "micromark-extension-mdx-jsx": ["micromark-extension-mdx-jsx@3.0.2", "", { "dependencies": { "@types/estree": "1.0.7", "devlop": "1.1.0", "estree-util-is-identifier-name": "3.0.0", "micromark-factory-mdx-expression": "2.0.3", "micromark-factory-space": "2.0.1", "micromark-util-character": "2.1.1", "micromark-util-events-to-acorn": "2.0.3", "micromark-util-symbol": "2.0.1", "micromark-util-types": "2.0.2", "vfile-message": "4.0.2" } }, "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ=="], + + "micromark-extension-mdx-md": ["micromark-extension-mdx-md@2.0.0", "", { "dependencies": { "micromark-util-types": "2.0.2" } }, "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ=="], + + "micromark-extension-mdxjs": ["micromark-extension-mdxjs@3.0.0", "", { "dependencies": { "acorn": "8.14.1", "acorn-jsx": "5.3.2", "micromark-extension-mdx-expression": "3.0.1", "micromark-extension-mdx-jsx": "3.0.2", "micromark-extension-mdx-md": "2.0.0", "micromark-extension-mdxjs-esm": "3.0.0", "micromark-util-combine-extensions": "2.0.1", "micromark-util-types": "2.0.2" } }, "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ=="], + + "micromark-extension-mdxjs-esm": ["micromark-extension-mdxjs-esm@3.0.0", "", { "dependencies": { "@types/estree": "1.0.7", "devlop": "1.1.0", "micromark-core-commonmark": "2.0.3", "micromark-util-character": "2.1.1", "micromark-util-events-to-acorn": "2.0.3", "micromark-util-symbol": "2.0.1", "micromark-util-types": "2.0.2", "unist-util-position-from-estree": "2.0.0", "vfile-message": "4.0.2" } }, "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A=="], + + "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "2.1.1", "micromark-util-symbol": "2.0.1", "micromark-util-types": "2.0.2" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="], + + "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "1.1.0", "micromark-util-character": "2.1.1", "micromark-util-symbol": "2.0.1", "micromark-util-types": "2.0.2" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="], + + "micromark-factory-mdx-expression": ["micromark-factory-mdx-expression@2.0.3", "", { "dependencies": { "@types/estree": "1.0.7", "devlop": "1.1.0", "micromark-factory-space": "2.0.1", "micromark-util-character": "2.1.1", "micromark-util-events-to-acorn": "2.0.3", "micromark-util-symbol": "2.0.1", "micromark-util-types": "2.0.2", "unist-util-position-from-estree": "2.0.0", "vfile-message": "4.0.2" } }, "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ=="], + + "micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "2.1.1", "micromark-util-types": "2.0.2" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="], + + "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "2.0.1", "micromark-util-character": "2.1.1", "micromark-util-symbol": "2.0.1", "micromark-util-types": "2.0.2" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="], + + "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "2.0.1", "micromark-util-character": "2.1.1", "micromark-util-symbol": "2.0.1", "micromark-util-types": "2.0.2" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="], + + "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "2.0.1", "micromark-util-types": "2.0.2" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], + + "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "2.0.1" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="], + + "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "2.1.1", "micromark-util-symbol": "2.0.1", "micromark-util-types": "2.0.2" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="], + + "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "2.0.1", "micromark-util-types": "2.0.2" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="], + + "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "2.0.1" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="], + + "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "1.1.0", "micromark-util-character": "2.1.1", "micromark-util-decode-numeric-character-reference": "2.0.2", "micromark-util-symbol": "2.0.1" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="], + + "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], + + "micromark-util-events-to-acorn": ["micromark-util-events-to-acorn@2.0.3", "", { "dependencies": { "@types/estree": "1.0.7", "@types/unist": "3.0.3", "devlop": "1.1.0", "estree-util-visit": "2.0.0", "micromark-util-symbol": "2.0.1", "micromark-util-types": "2.0.2", "vfile-message": "4.0.2" } }, "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg=="], + + "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="], + + "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "2.0.1" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="], + + "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "2.0.2" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="], + + "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "2.1.1", "micromark-util-encode": "2.0.1", "micromark-util-symbol": "2.0.1" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], + + "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "1.1.0", "micromark-util-chunked": "2.0.1", "micromark-util-symbol": "2.0.1", "micromark-util-types": "2.0.2" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="], + + "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], + + "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "3.0.3", "picomatch": "2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], + + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + + "mimic-fn": ["mimic-fn@4.0.0", "", {}, "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="], + + "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], + + "minimatch": ["minimatch@3.1.4", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-twmL+S8+7yIsE9wsqgzU3E8/LumN3M3QELrBZ20OdmQ9jB2JvW5oZtBEmft84k/Gs5CG9mqtWc6Y9vW+JEzGxw=="], + + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + + "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + + "mixin-deep": ["mixin-deep@1.3.2", "", { "dependencies": { "for-in": "1.0.2", "is-extendable": "1.0.1" } }, "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA=="], + + "mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "1.2.8" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="], + + "mongodb": ["mongodb@6.20.0", "", { "dependencies": { "@mongodb-js/saslprep": "1.3.2", "bson": "6.10.4", "mongodb-connection-string-url": "3.0.2" }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.188.0", "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", "gcp-metadata": "^5.2.0", "kerberos": "^2.0.1", "mongodb-client-encryption": ">=6.0.0 <7", "snappy": "^7.3.2", "socks": "^2.7.1" }, "optionalPeers": ["@aws-sdk/credential-providers", "@mongodb-js/zstd", "gcp-metadata", "kerberos", "mongodb-client-encryption", "snappy", "socks"] }, "sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ=="], + + "mongodb-connection-string-url": ["mongodb-connection-string-url@3.0.2", "", { "dependencies": { "@types/whatwg-url": "11.0.5", "whatwg-url": "14.2.0" } }, "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA=="], + + "motion": ["motion@12.38.0", "", { "dependencies": { "framer-motion": "12.38.0", "tslib": "2.8.1" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-uYfXzeHlgThchzwz5Te47dlv5JOUC7OB4rjJ/7XTUgtBZD8CchMN8qEJ4ZVsUmTyYA44zjV0fBwsiktRuFnn+w=="], + + "motion-dom": ["motion-dom@12.38.0", "", { "dependencies": { "motion-utils": "12.36.0" } }, "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA=="], + + "motion-utils": ["motion-utils@12.36.0", "", {}, "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg=="], + + "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "multer": ["multer@1.4.5-lts.2", "", { "dependencies": { "append-field": "1.0.0", "busboy": "1.6.0", "concat-stream": "1.6.2", "mkdirp": "0.5.6", "object-assign": "4.1.1", "type-is": "1.6.18", "xtend": "4.0.2" } }, "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A=="], + + "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "1.3.0", "object-assign": "4.1.1", "thenify-all": "1.6.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], + + "nan": ["nan@2.22.2", "", {}, "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ=="], + + "nanoid": ["nanoid@3.3.18", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w=="], + + "nanomatch": ["nanomatch@1.2.13", "", { "dependencies": { "arr-diff": "4.0.0", "array-unique": "0.3.2", "define-property": "2.0.2", "extend-shallow": "3.0.2", "fragment-cache": "0.2.1", "is-windows": "1.0.2", "kind-of": "6.0.3", "object.pick": "1.3.0", "regex-not": "1.0.2", "snapdragon": "0.8.2", "to-regex": "3.0.2" } }, "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA=="], + + "nanostores": ["nanostores@1.5.4", "", {}, "sha512-4UXY7OAJEK0Y6V3SudBU7cXjAzTDc189R78dDOh0Xbm19gpKYto6WizSg58LJf12lTsRxKSLGyE117OiLpM4DQ=="], + + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + + "next": ["next@16.3.6", "", { "dependencies": { "@next/env": "16.3.6", "@swc/helpers": "0.5.23", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", "postcss": "8.5.23", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.3.6", "@next/swc-darwin-x64": "16.3.6", "@next/swc-linux-arm64-gnu": "16.3.6", "@next/swc-linux-arm64-musl": "16.3.6", "@next/swc-linux-x64-gnu": "16.3.6", "@next/swc-linux-x64-musl": "16.3.6", "@next/swc-win32-arm64-msvc": "16.3.6", "@next/swc-win32-x64-msvc": "16.3.6", "sharp": "^0.35.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-L+otWM/aQbYTx98aZhgEoMb4bZAXx1YVW4UMA/vuCyCoWG5HJyZUili8QAkqzrcC+5///tsz3s0M+SlyB5bLMw=="], + + "next-themes": ["next-themes@0.2.1", "", { "peerDependencies": { "next": "*", "react": "*", "react-dom": "*" } }, "sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A=="], + + "nice-try": ["nice-try@1.0.5", "", {}, "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="], + + "node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="], + + "node-notifier": ["node-notifier@5.4.5", "", { "dependencies": { "growly": "1.3.0", "is-wsl": "1.1.0", "semver": "5.7.2", "shellwords": "0.1.1", "which": "1.3.1" } }, "sha512-tVbHs7DyTLtzOiN78izLA85zRqB9NvEXkAf014Vx3jtSvn/xBl6bR8ZYifj+dFcFrKI21huSQgJZ6ZtL3B4HfQ=="], + + "node-releases": ["node-releases@2.0.57", "", {}, "sha512-kQK9LGGFiHtrWiNhZtA7Qbw17AQz+dmsEKODRIVTXA9+e5MS/2gZEBhYJt13GrAz5/IOZKddH/0Z3TP/Zgo+yw=="], + + "nodemailer": ["nodemailer@6.10.0", "", {}, "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA=="], + + "normalize-package-data": ["normalize-package-data@2.5.0", "", { "dependencies": { "hosted-git-info": "2.8.9", "resolve": "1.22.10", "semver": "5.7.2", "validate-npm-package-license": "3.0.4" } }, "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA=="], + + "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], + + "normalize-range": ["normalize-range@0.1.2", "", {}, "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA=="], + + "npm-run-path": ["npm-run-path@5.3.0", "", { "dependencies": { "path-key": "4.0.0" } }, "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ=="], + + "npm-to-yarn": ["npm-to-yarn@3.0.1", "", {}, "sha512-tt6PvKu4WyzPwWUzy/hvPFqn+uwXO0K1ZHka8az3NnrhWJDmSqI8ncWq0fkL0k/lmmi5tAC11FXwXuh0rFbt1A=="], + + "nwsapi": ["nwsapi@2.2.20", "", {}, "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA=="], + + "oauth-sign": ["oauth-sign@0.9.0", "", {}, "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "object-copy": ["object-copy@0.1.0", "", { "dependencies": { "copy-descriptor": "0.1.1", "define-property": "0.2.5", "kind-of": "3.2.2" } }, "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ=="], + + "object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], + + "object-visit": ["object-visit@1.0.1", "", { "dependencies": { "isobject": "3.0.1" } }, "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA=="], + + "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "define-properties": "1.2.1", "es-object-atoms": "1.1.1", "has-symbols": "1.1.0", "object-keys": "1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="], + + "object.entries": ["object.entries@1.1.9", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "define-properties": "1.2.1", "es-object-atoms": "1.1.1" } }, "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw=="], + + "object.fromentries": ["object.fromentries@2.0.8", "", { "dependencies": { "call-bind": "1.0.8", "define-properties": "1.2.1", "es-abstract": "1.23.9", "es-object-atoms": "1.1.1" } }, "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ=="], + + "object.getownpropertydescriptors": ["object.getownpropertydescriptors@2.1.8", "", { "dependencies": { "array.prototype.reduce": "1.0.8", "call-bind": "1.0.8", "define-properties": "1.2.1", "es-abstract": "1.23.9", "es-object-atoms": "1.1.1", "gopd": "1.2.0", "safe-array-concat": "1.1.3" } }, "sha512-qkHIGe4q0lSYMv0XI4SsBTJz3WaURhLvd0lKSgtVuOsJ2krg4SgMw3PIRQFMp07yi++UR3se2mkcLqsBNpBb/A=="], + + "object.groupby": ["object.groupby@1.0.3", "", { "dependencies": { "call-bind": "1.0.8", "define-properties": "1.2.1", "es-abstract": "1.23.9" } }, "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ=="], + + "object.pick": ["object.pick@1.3.0", "", { "dependencies": { "isobject": "3.0.1" } }, "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ=="], + + "object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "define-properties": "1.2.1", "es-object-atoms": "1.1.1" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="], + + "on-exit-leak-free": ["on-exit-leak-free@2.1.2", "", {}, "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="], + + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1.0.2" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + + "onetime": ["onetime@6.0.0", "", { "dependencies": { "mimic-fn": "4.0.0" } }, "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="], + + "oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="], + + "oniguruma-to-es": ["oniguruma-to-es@4.3.5", "", { "dependencies": { "oniguruma-parser": "0.12.1", "regex": "6.1.0", "regex-recursion": "6.0.2" } }, "sha512-Zjygswjpsewa0NLTsiizVuMQZbp0MDyM6lIt66OxsF21npUDlzpHi1Mgb/qhQdkb+dWFTzJmFbEWdvZgRho8eQ=="], + + "openapi-sampler": ["openapi-sampler@1.7.2", "", { "dependencies": { "@types/json-schema": "7.0.15", "fast-xml-parser": "5.5.8", "json-pointer": "0.6.2" } }, "sha512-OKytvqB5XIaTgA9xtw8W8UTar+uymW2xPVpFN0NihMtuHPdPTGxBEhGnfFnJW5g/gOSIvkP+H0Xh3XhVI9/n7g=="], + + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "0.1.4", "fast-levenshtein": "2.0.6", "levn": "0.4.1", "prelude-ls": "1.2.1", "type-check": "0.4.0", "word-wrap": "1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "outdent": ["outdent@0.5.0", "", {}, "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q=="], + + "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "1.3.0", "object-keys": "1.1.1", "safe-push-apply": "1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], + + "p-each-series": ["p-each-series@1.0.0", "", { "dependencies": { "p-reduce": "1.0.0" } }, "sha512-J/e9xiZZQNrt+958FFzJ+auItsBGq+UrQ7nE89AUP7UOTtjHnkISANXLdayhVzh538UnLMCSlf13lFfRIAKQOA=="], + + "p-filter": ["p-filter@2.1.0", "", { "dependencies": { "p-map": "2.1.0" } }, "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw=="], + + "p-finally": ["p-finally@1.0.0", "", {}, "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow=="], + + "p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "2.2.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "3.1.0" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "p-map": ["p-map@2.1.0", "", {}, "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw=="], + + "p-reduce": ["p-reduce@1.0.0", "", {}, "sha512-3Tx1T3oM1xO/Y8Gj0sWyE78EIJZ+t+aEmXUdvQgvGmSMri7aPTHoovbXEreWKkL5j21Er60XAWLTzKbAKYOujQ=="], + + "p-try": ["p-try@2.2.0", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="], + + "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], + + "package-manager-detector": ["package-manager-detector@0.2.11", "", { "dependencies": { "quansync": "0.2.10" } }, "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "3.1.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "2.0.11", "character-entities-legacy": "3.0.0", "character-reference-invalid": "2.0.1", "decode-named-character-reference": "1.1.0", "is-alphanumerical": "2.0.1", "is-decimal": "2.0.1", "is-hexadecimal": "2.0.1" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], + + "parse-json": ["parse-json@4.0.0", "", { "dependencies": { "error-ex": "1.3.2", "json-parse-better-errors": "1.0.2" } }, "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw=="], + + "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "6.0.1" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], + + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + + "pascalcase": ["pascalcase@0.1.1", "", {}, "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw=="], + + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-expression-matcher": ["path-expression-matcher@1.6.2", "", {}, "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ=="], + + "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "10.4.3", "minipass": "7.1.2" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + + "path-to-regexp": ["path-to-regexp@0.1.13", "", {}, "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA=="], + + "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], + + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "performance-now": ["performance-now@2.1.0", "", {}, "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="], + + "pg": ["pg@8.23.0", "", { "dependencies": { "pg-connection-string": "^2.14.0", "pg-pool": "^3.14.0", "pg-protocol": "^1.16.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.4.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-Ip2EQCngowJLGOfCwkFhPXU7/ljlhn6Rxlmy4XYfL2Y+vyRM59+8uR2xqRWKdYmbXmxCFOAmKxBuSUCdF34qLg=="], + + "pg-cloudflare": ["pg-cloudflare@1.4.1", "", {}, "sha512-6PQbsFWZcp9EmJEwy5cGQ2La+AMWpP46lgbb8X+U/XsHIUweYDNCpeuKck5RxL2MdVFi7krbbEi5nX4Zh7JhrQ=="], + + "pg-connection-string": ["pg-connection-string@2.14.1", "", {}, "sha512-qR3kGNPBLpCNtz0evbKA0Y/MRFXwSSdT+pTJvYp/bXTcReZbvX1kzF0IyTc1QnxqF7AZbOeBhNL8R5mYQZV/MA=="], + + "pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="], + + "pg-pool": ["pg-pool@3.14.0", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw=="], + + "pg-protocol": ["pg-protocol@1.16.1", "", {}, "sha512-p9VOFMiHB/ZbJATetbg+99PxssTVSQRnyuPSQ67mN1+1KBOjZaZ83ZQzltnxPhJwSsC3nwVjJ10DVJlerbFzLg=="], + + "pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="], + + "pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], + + "pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="], + + "pify": ["pify@4.0.1", "", {}, "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="], + + "pino": ["pino@9.14.0", "", { "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^3.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w=="], + + "pino-abstract-transport": ["pino-abstract-transport@2.0.0", "", { "dependencies": { "split2": "4.2.0" } }, "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw=="], + + "pino-std-serializers": ["pino-std-serializers@7.0.0", "", {}, "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA=="], + + "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="], + + "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], + + "pkg-dir": ["pkg-dir@3.0.0", "", { "dependencies": { "find-up": "3.0.0" } }, "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw=="], + + "pn": ["pn@1.1.0", "", {}, "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA=="], + + "posix-character-classes": ["posix-character-classes@0.1.1", "", {}, "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg=="], + + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], + + "postcss": ["postcss@8.5.23", "", { "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg=="], + + "postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "4.2.0", "read-cache": "1.0.0", "resolve": "1.22.10" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="], + + "postcss-js": ["postcss-js@4.0.1", "", { "dependencies": { "camelcase-css": "2.0.1" }, "peerDependencies": { "postcss": "^8.4.21" } }, "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw=="], + + "postcss-load-config": ["postcss-load-config@6.0.1", "", { "dependencies": { "lilconfig": "3.1.3" }, "peerDependencies": { "jiti": ">=1.21.0", "postcss": ">=8.0.9", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["jiti", "postcss", "tsx", "yaml"] }, "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g=="], + + "postcss-nested": ["postcss-nested@6.2.0", "", { "dependencies": { "postcss-selector-parser": "6.1.2" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ=="], + + "postcss-selector-parser": ["postcss-selector-parser@6.1.3", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-cDoO18VWCIWRsSaws3C23b0HXRxlUknttfdNcbXnf3IGHAPRNLlXPHc6dYiatOkxW0W4uZh524Plaaw5a7WEtQ=="], + + "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], + + "postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="], + + "postgres-bytea": ["postgres-bytea@1.0.1", "", {}, "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ=="], + + "postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="], + + "postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="], + + "posthog-js": ["posthog-js@1.435.6", "", { "dependencies": { "@posthog/browser-common": "^0.9.1", "@posthog/core": "^1.55.3", "@posthog/types": "^1.413.0", "core-js": "^3.49.0", "dompurify": "^3.4.13", "fflate": "^0.4.8", "preact": "^10.29.3", "query-selector-shadow-dom": "^1.0.1", "web-vitals": "^6.2.1", "web-vitals-soft-navs": "npm:web-vitals@6.2.1" }, "peerDependencies": { "@types/react": ">=16.8.0", "react": ">=16.8.0" }, "optionalPeers": ["@types/react", "react"] }, "sha512-PdBZw1Np4Ch2ZzvQ/Ij0CTBrJSkSeEmDJsyJfbaipTYH8XJKJk45BYLtNgL4z9DlevRcYJxhwb15VrDzbw7shA=="], + + "preact": ["preact@10.29.8", "", { "peerDependencies": { "preact-render-to-string": ">=5" }, "optionalPeers": ["preact-render-to-string"] }, "sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "prettier": ["prettier@3.5.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw=="], + + "pretty-format": ["pretty-format@24.9.0", "", { "dependencies": { "@jest/types": "24.9.0", "ansi-regex": "4.1.1", "ansi-styles": "3.2.1", "react-is": "16.13.1" } }, "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA=="], + + "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], + + "process-warning": ["process-warning@5.0.0", "", {}, "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA=="], + + "prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "3.0.3", "sisteransi": "1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="], + + "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "1.4.0", "object-assign": "4.1.1", "react-is": "16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], + + "proper-lockfile": ["proper-lockfile@4.1.2", "", { "dependencies": { "graceful-fs": "4.2.11", "retry": "0.12.0", "signal-exit": "3.0.7" } }, "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA=="], + + "property-information": ["property-information@7.0.0", "", {}, "sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg=="], + + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + + "psl": ["psl@1.15.0", "", { "dependencies": { "punycode": "2.3.1" } }, "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w=="], + + "pump": ["pump@3.0.2", "", { "dependencies": { "end-of-stream": "1.4.4", "once": "1.4.0" } }, "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "qs": ["qs@6.16.0", "", { "dependencies": { "es-define-property": "^1.0.1", "side-channel": "^1.1.1" } }, "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA=="], + + "quansync": ["quansync@0.2.10", "", {}, "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A=="], + + "query-selector-shadow-dom": ["query-selector-shadow-dom@1.0.1", "", {}, "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw=="], + + "querystringify": ["querystringify@2.2.0", "", {}, "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="], + + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.1", "iconv-lite": "0.7.2", "unpipe": "1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], + + "react": ["react@19.2.8", "", {}, "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw=="], + + "react-dom": ["react-dom@19.2.8", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.8" } }, "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ=="], + + "react-hook-form": ["react-hook-form@7.71.2", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-1CHvcDYzuRUNOflt4MOq3ZM46AronNJtQ1S7tnX6YN4y72qhgiUItpacZUAQ0TyWYci3yz1X+rXaSxiuEm86PA=="], + + "react-icons": ["react-icons@4.12.0", "", { "peerDependencies": { "react": "*" } }, "sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw=="], + + "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + + "react-medium-image-zoom": ["react-medium-image-zoom@5.4.1", "", { "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" } }, "sha512-DD2iZYaCfAwiQGR8AN62r/cDJYoXhezlYJc5HY4TzBUGuGge43CptG0f7m0PEIM72aN6GfpjohvY1yYdtCJB7g=="], + + "react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "2.3.8", "react-style-singleton": "2.2.3", "tslib": "2.8.1", "use-callback-ref": "1.3.3", "use-sidecar": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="], + + "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "2.2.3", "tslib": "2.8.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], + + "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "1.0.1", "tslib": "2.8.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], + + "read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="], + + "read-pkg": ["read-pkg@3.0.0", "", { "dependencies": { "load-json-file": "4.0.0", "normalize-package-data": "2.5.0", "path-type": "3.0.0" } }, "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA=="], + + "read-pkg-up": ["read-pkg-up@4.0.0", "", { "dependencies": { "find-up": "3.0.0", "read-pkg": "3.0.0" } }, "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA=="], + + "read-yaml-file": ["read-yaml-file@1.1.0", "", { "dependencies": { "graceful-fs": "4.2.11", "js-yaml": "3.14.1", "pify": "4.0.1", "strip-bom": "3.0.0" } }, "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA=="], + + "readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "1.0.2", "inherits": "2.0.4", "isarray": "1.0.0", "process-nextick-args": "2.0.1", "safe-buffer": "5.1.2", "string_decoder": "1.1.1", "util-deprecate": "1.0.2" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + + "real-require": ["real-require@0.2.0", "", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="], + + "realpath-native": ["realpath-native@1.1.0", "", { "dependencies": { "util.promisify": "1.1.3" } }, "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA=="], + + "recma-build-jsx": ["recma-build-jsx@1.0.0", "", { "dependencies": { "@types/estree": "1.0.7", "estree-util-build-jsx": "3.0.1", "vfile": "6.0.3" } }, "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew=="], + + "recma-jsx": ["recma-jsx@1.0.0", "", { "dependencies": { "acorn-jsx": "5.3.2", "estree-util-to-js": "2.0.0", "recma-parse": "1.0.0", "recma-stringify": "1.0.0", "unified": "11.0.5" } }, "sha512-5vwkv65qWwYxg+Atz95acp8DMu1JDSqdGkA2Of1j6rCreyFUE/gp15fC8MnGEuG1W68UKjM6x6+YTWIh7hZM/Q=="], + + "recma-parse": ["recma-parse@1.0.0", "", { "dependencies": { "@types/estree": "1.0.7", "esast-util-from-js": "2.0.1", "unified": "11.0.5", "vfile": "6.0.3" } }, "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ=="], + + "recma-stringify": ["recma-stringify@1.0.0", "", { "dependencies": { "@types/estree": "1.0.7", "estree-util-to-js": "2.0.0", "unified": "11.0.5", "vfile": "6.0.3" } }, "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g=="], + + "redis-errors": ["redis-errors@1.2.0", "", {}, "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w=="], + + "redis-parser": ["redis-parser@3.0.0", "", { "dependencies": { "redis-errors": "1.2.0" } }, "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A=="], + + "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "1.0.8", "define-properties": "1.2.1", "es-abstract": "1.23.9", "es-errors": "1.3.0", "es-object-atoms": "1.1.1", "get-intrinsic": "1.3.0", "get-proto": "1.0.1", "which-builtin-type": "1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="], + + "regenerator-runtime": ["regenerator-runtime@0.14.1", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="], + + "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="], + + "regex-not": ["regex-not@1.0.2", "", { "dependencies": { "extend-shallow": "3.0.2", "safe-regex": "1.1.0" } }, "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A=="], + + "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="], + + "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], + + "regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "1.0.8", "define-properties": "1.2.1", "es-errors": "1.3.0", "get-proto": "1.0.1", "gopd": "1.2.0", "set-function-name": "2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="], + + "rehype-raw": ["rehype-raw@7.0.0", "", { "dependencies": { "@types/hast": "3.0.4", "hast-util-raw": "9.1.0", "vfile": "6.0.3" } }, "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww=="], + + "rehype-recma": ["rehype-recma@1.0.0", "", { "dependencies": { "@types/estree": "1.0.7", "@types/hast": "3.0.4", "hast-util-to-estree": "3.1.3" } }, "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw=="], + + "remark": ["remark@15.0.1", "", { "dependencies": { "@types/mdast": "4.0.4", "remark-parse": "11.0.0", "remark-stringify": "11.0.0", "unified": "11.0.5" } }, "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A=="], + + "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "4.0.4", "mdast-util-gfm": "3.1.0", "micromark-extension-gfm": "3.0.0", "remark-parse": "11.0.0", "remark-stringify": "11.0.0", "unified": "11.0.5" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], + + "remark-mdx": ["remark-mdx@3.1.0", "", { "dependencies": { "mdast-util-mdx": "3.0.0", "micromark-extension-mdxjs": "3.0.0" } }, "sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA=="], + + "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "4.0.4", "mdast-util-from-markdown": "2.0.2", "micromark-util-types": "2.0.2", "unified": "11.0.5" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], + + "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "3.0.4", "@types/mdast": "4.0.4", "mdast-util-to-hast": "13.2.0", "unified": "11.0.5", "vfile": "6.0.3" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="], + + "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "4.0.4", "mdast-util-to-markdown": "2.1.2", "unified": "11.0.5" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], + + "remove-trailing-separator": ["remove-trailing-separator@1.1.0", "", {}, "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw=="], + + "repeat-element": ["repeat-element@1.1.4", "", {}, "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ=="], + + "repeat-string": ["repeat-string@1.6.1", "", {}, "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w=="], + + "request": ["request@2.88.2", "", { "dependencies": { "aws-sign2": "0.7.0", "aws4": "1.13.2", "caseless": "0.12.0", "combined-stream": "1.0.8", "extend": "3.0.2", "forever-agent": "0.6.1", "form-data": "2.3.3", "har-validator": "5.1.5", "http-signature": "1.2.0", "is-typedarray": "1.0.0", "isstream": "0.1.2", "json-stringify-safe": "5.0.1", "mime-types": "2.1.35", "oauth-sign": "0.9.0", "performance-now": "2.1.0", "qs": "6.5.3", "safe-buffer": "5.2.1", "tough-cookie": "2.5.0", "tunnel-agent": "0.6.0", "uuid": "3.4.0" } }, "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw=="], + + "request-promise-core": ["request-promise-core@1.1.4", "", { "dependencies": { "lodash": "4.17.21" }, "peerDependencies": { "request": "^2.34" } }, "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw=="], + + "request-promise-native": ["request-promise-native@1.0.9", "", { "dependencies": { "request-promise-core": "1.1.4", "stealthy-require": "1.1.1", "tough-cookie": "2.5.0" }, "peerDependencies": { "request": "^2.34" } }, "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g=="], + + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + + "require-main-filename": ["require-main-filename@2.0.0", "", {}, "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="], + + "requires-port": ["requires-port@1.0.0", "", {}, "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="], + + "resolve": ["resolve@2.0.0-next.5", "", { "dependencies": { "is-core-module": "2.16.1", "path-parse": "1.0.7", "supports-preserve-symlinks-flag": "1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA=="], + + "resolve-cwd": ["resolve-cwd@2.0.0", "", { "dependencies": { "resolve-from": "3.0.0" } }, "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg=="], + + "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], + + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + + "resolve-url": ["resolve-url@0.2.1", "", {}, "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg=="], + + "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "7.0.0", "signal-exit": "4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], + + "ret": ["ret@0.1.15", "", {}, "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="], + + "retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], + + "rimraf": ["rimraf@2.7.1", "", { "dependencies": { "glob": "7.2.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w=="], + + "rollup": ["rollup@4.59.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.59.0", "@rollup/rollup-android-arm64": "4.59.0", "@rollup/rollup-darwin-arm64": "4.59.0", "@rollup/rollup-darwin-x64": "4.59.0", "@rollup/rollup-freebsd-arm64": "4.59.0", "@rollup/rollup-freebsd-x64": "4.59.0", "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", "@rollup/rollup-linux-arm-musleabihf": "4.59.0", "@rollup/rollup-linux-arm64-gnu": "4.59.0", "@rollup/rollup-linux-arm64-musl": "4.59.0", "@rollup/rollup-linux-loong64-gnu": "4.59.0", "@rollup/rollup-linux-loong64-musl": "4.59.0", "@rollup/rollup-linux-ppc64-gnu": "4.59.0", "@rollup/rollup-linux-ppc64-musl": "4.59.0", "@rollup/rollup-linux-riscv64-gnu": "4.59.0", "@rollup/rollup-linux-riscv64-musl": "4.59.0", "@rollup/rollup-linux-s390x-gnu": "4.59.0", "@rollup/rollup-linux-x64-gnu": "4.59.0", "@rollup/rollup-linux-x64-musl": "4.59.0", "@rollup/rollup-openbsd-x64": "4.59.0", "@rollup/rollup-openharmony-arm64": "4.59.0", "@rollup/rollup-win32-arm64-msvc": "4.59.0", "@rollup/rollup-win32-ia32-msvc": "4.59.0", "@rollup/rollup-win32-x64-gnu": "4.59.0", "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg=="], + + "rou3": ["rou3@0.9.2", "", {}, "sha512-3SOzvaAg8rkHrXtRjpCvCvbyO5to9oOO27Z/XqHEYXfMRVSw/qMIVdmaOk9W2lcRLtR6dlqTjo9hDeJk70QBYQ=="], + + "router": ["router@2.2.0", "", { "dependencies": { "debug": "4.4.3", "depd": "2.0.0", "is-promise": "4.0.0", "parseurl": "1.3.3", "path-to-regexp": "8.3.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], + + "rsvp": ["rsvp@4.8.5", "", {}, "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "1.2.3" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "get-intrinsic": "1.3.0", "has-symbols": "1.1.0", "isarray": "2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "1.3.0", "isarray": "2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="], + + "safe-regex": ["safe-regex@1.1.0", "", { "dependencies": { "ret": "0.1.15" } }, "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg=="], + + "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "1.0.4", "es-errors": "1.3.0", "is-regex": "1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], + + "safe-stable-stringify": ["safe-stable-stringify@2.5.0", "", {}, "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "sane": ["sane@4.1.0", "", { "dependencies": { "@cnakazawa/watch": "1.0.4", "anymatch": "2.0.0", "capture-exit": "2.0.0", "exec-sh": "0.3.6", "execa": "1.0.0", "fb-watchman": "2.0.2", "micromatch": "3.1.10", "minimist": "1.2.8", "walker": "1.0.8" }, "bin": { "sane": "./src/cli.js" } }, "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA=="], + + "sax": ["sax@1.4.1", "", {}, "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg=="], + + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + + "scroll-into-view-if-needed": ["scroll-into-view-if-needed@3.1.0", "", { "dependencies": { "compute-scroll-into-view": "3.1.1" } }, "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ=="], + + "semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], + + "send": ["send@0.19.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "1.0.2", "escape-html": "1.0.3", "etag": "1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "1.2.1", "statuses": "2.0.1" } }, "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw=="], + + "serve-static": ["serve-static@1.16.2", "", { "dependencies": { "encodeurl": "2.0.0", "escape-html": "1.0.3", "parseurl": "1.3.3", "send": "0.19.0" } }, "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw=="], + + "set-blocking": ["set-blocking@2.0.0", "", {}, "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="], + + "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="], + + "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "1.1.4", "es-errors": "1.3.0", "function-bind": "1.1.2", "get-intrinsic": "1.3.0", "gopd": "1.2.0", "has-property-descriptors": "1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], + + "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "1.1.4", "es-errors": "1.3.0", "functions-have-names": "1.2.3", "has-property-descriptors": "1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="], + + "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "1.0.1", "es-errors": "1.3.0", "es-object-atoms": "1.1.1" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="], + + "set-value": ["set-value@2.0.1", "", { "dependencies": { "extend-shallow": "2.0.1", "is-extendable": "0.1.1", "is-plain-object": "2.0.4", "split-string": "3.1.0" } }, "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw=="], + + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + + "sharp": ["sharp@0.35.5", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.5", "@img/sharp-darwin-x64": "0.35.5", "@img/sharp-freebsd-wasm32": "0.35.5", "@img/sharp-libvips-darwin-arm64": "1.3.4", "@img/sharp-libvips-darwin-x64": "1.3.4", "@img/sharp-libvips-linux-arm": "1.3.4", "@img/sharp-libvips-linux-arm64": "1.3.4", "@img/sharp-libvips-linux-ppc64": "1.3.4", "@img/sharp-libvips-linux-riscv64": "1.3.4", "@img/sharp-libvips-linux-s390x": "1.3.4", "@img/sharp-libvips-linux-x64": "1.3.4", "@img/sharp-libvips-linuxmusl-arm64": "1.3.4", "@img/sharp-libvips-linuxmusl-x64": "1.3.4", "@img/sharp-linux-arm": "0.35.5", "@img/sharp-linux-arm64": "0.35.5", "@img/sharp-linux-ppc64": "0.35.5", "@img/sharp-linux-riscv64": "0.35.5", "@img/sharp-linux-s390x": "0.35.5", "@img/sharp-linux-x64": "0.35.5", "@img/sharp-linuxmusl-arm64": "0.35.5", "@img/sharp-linuxmusl-x64": "0.35.5", "@img/sharp-webcontainers-wasm32": "0.35.5", "@img/sharp-win32-arm64": "0.35.5", "@img/sharp-win32-ia32": "0.35.5", "@img/sharp-win32-x64": "0.35.5" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "shellwords": ["shellwords@0.1.1", "", {}, "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww=="], + + "shiki": ["shiki@4.0.2", "", { "dependencies": { "@shikijs/core": "4.0.2", "@shikijs/engine-javascript": "4.0.2", "@shikijs/engine-oniguruma": "4.0.2", "@shikijs/langs": "4.0.2", "@shikijs/themes": "4.0.2", "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "10.0.2", "@types/hast": "3.0.4" } }, "sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ=="], + + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "1.3.0", "object-inspect": "1.13.4", "side-channel-list": "1.0.0", "side-channel-map": "1.0.1", "side-channel-weakmap": "1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "1.3.0", "object-inspect": "1.13.4" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "1.0.4", "es-errors": "1.3.0", "get-intrinsic": "1.3.0", "object-inspect": "1.13.4" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "1.0.4", "es-errors": "1.3.0", "get-intrinsic": "1.3.0", "object-inspect": "1.13.4", "side-channel-map": "1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], + + "slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], + + "slice-ansi": ["slice-ansi@5.0.0", "", { "dependencies": { "ansi-styles": "6.2.1", "is-fullwidth-code-point": "4.0.0" } }, "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ=="], + + "snapdragon": ["snapdragon@0.8.2", "", { "dependencies": { "base": "0.11.2", "debug": "2.6.9", "define-property": "0.2.5", "extend-shallow": "2.0.1", "map-cache": "0.2.2", "source-map": "0.5.7", "source-map-resolve": "0.5.3", "use": "3.1.1" } }, "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg=="], + + "snapdragon-node": ["snapdragon-node@2.1.1", "", { "dependencies": { "define-property": "1.0.0", "isobject": "3.0.1", "snapdragon-util": "3.0.1" } }, "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw=="], + + "snapdragon-util": ["snapdragon-util@3.0.1", "", { "dependencies": { "kind-of": "3.2.2" } }, "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ=="], + + "sonic-boom": ["sonic-boom@4.2.0", "", { "dependencies": { "atomic-sleep": "1.0.0" } }, "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww=="], + + "sonner": ["sonner@1.7.4", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw=="], + + "source-map": ["source-map@0.8.0-beta.0", "", { "dependencies": { "whatwg-url": "7.1.0" } }, "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "source-map-resolve": ["source-map-resolve@0.5.3", "", { "dependencies": { "atob": "2.1.2", "decode-uri-component": "0.2.2", "resolve-url": "0.2.1", "source-map-url": "0.4.1", "urix": "0.1.0" } }, "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw=="], + + "source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "1.1.2", "source-map": "0.6.1" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="], + + "source-map-url": ["source-map-url@0.4.1", "", {}, "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw=="], + + "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + + "sparse-bitfield": ["sparse-bitfield@3.0.3", "", { "dependencies": { "memory-pager": "1.5.0" } }, "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ=="], + + "spawndamnit": ["spawndamnit@3.0.1", "", { "dependencies": { "cross-spawn": "7.0.6", "signal-exit": "4.1.0" } }, "sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg=="], + + "spdx-correct": ["spdx-correct@3.2.0", "", { "dependencies": { "spdx-expression-parse": "3.0.1", "spdx-license-ids": "3.0.21" } }, "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA=="], + + "spdx-exceptions": ["spdx-exceptions@2.5.0", "", {}, "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="], + + "spdx-expression-parse": ["spdx-expression-parse@3.0.1", "", { "dependencies": { "spdx-exceptions": "2.5.0", "spdx-license-ids": "3.0.21" } }, "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q=="], + + "spdx-license-ids": ["spdx-license-ids@3.0.21", "", {}, "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg=="], + + "split-string": ["split-string@3.1.0", "", { "dependencies": { "extend-shallow": "3.0.2" } }, "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw=="], + + "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="], + + "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], + + "srvx": ["srvx@0.11.22", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-LqZxxBDMKuMAZzFzJnDCkFOrs9MZQZr0LvHiO/SuSZVdQaXD7xQ5UWTUxheJrQPve1qk9MG2B/yttUvJxw8egQ=="], + + "sshpk": ["sshpk@1.18.0", "", { "dependencies": { "asn1": "0.2.6", "assert-plus": "1.0.0", "bcrypt-pbkdf": "1.0.2", "dashdash": "1.14.1", "ecc-jsbn": "0.1.2", "getpass": "0.1.7", "jsbn": "0.1.1", "safer-buffer": "2.1.2", "tweetnacl": "0.14.5" }, "bin": { "sshpk-conv": "bin/sshpk-conv", "sshpk-sign": "bin/sshpk-sign", "sshpk-verify": "bin/sshpk-verify" } }, "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ=="], + + "stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="], + + "stack-utils": ["stack-utils@1.0.5", "", { "dependencies": { "escape-string-regexp": "2.0.0" } }, "sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ=="], + + "standard-as-callback": ["standard-as-callback@2.1.0", "", {}, "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="], + + "standardwebhooks": ["standardwebhooks@1.1.1", "", { "dependencies": { "@stablelib/base64": "^1.0.0", "fast-sha256": "^1.3.0" } }, "sha512-bCbX9ZEyFkWPsRz7Bl3NuQUJohmwGSev/yhr7vhaGPlc4AfIrspIRa6cPTBuI1ItmrTDJ4d/S2hCsfe4+vQGnQ=="], + + "static-extend": ["static-extend@0.1.2", "", { "dependencies": { "define-property": "0.2.5", "object-copy": "0.1.0" } }, "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g=="], + + "statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], + + "stealthy-require": ["stealthy-require@1.1.1", "", {}, "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g=="], + + "streamsearch": ["streamsearch@1.1.0", "", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="], + + "string-argv": ["string-argv@0.3.2", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="], + + "string-length": ["string-length@2.0.0", "", { "dependencies": { "astral-regex": "1.0.0", "strip-ansi": "4.0.0" } }, "sha512-Qka42GGrS8Mm3SZ+7cH8UXiIWI867/b/Z/feQSpQx/rbfB8UGknGEZVaUQMOUVj+soY6NpWAxily63HI1OckVQ=="], + + "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "10.4.0", "get-east-asian-width": "1.3.0", "strip-ansi": "7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + + "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "8.0.0", "is-fullwidth-code-point": "3.0.0", "strip-ansi": "6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "string.prototype.includes": ["string.prototype.includes@2.0.1", "", { "dependencies": { "call-bind": "1.0.8", "define-properties": "1.2.1", "es-abstract": "1.23.9" } }, "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg=="], + + "string.prototype.matchall": ["string.prototype.matchall@4.0.12", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "define-properties": "1.2.1", "es-abstract": "1.23.9", "es-errors": "1.3.0", "es-object-atoms": "1.1.1", "get-intrinsic": "1.3.0", "gopd": "1.2.0", "has-symbols": "1.1.0", "internal-slot": "1.1.0", "regexp.prototype.flags": "1.5.4", "set-function-name": "2.0.2", "side-channel": "1.1.0" } }, "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA=="], + + "string.prototype.repeat": ["string.prototype.repeat@1.0.0", "", { "dependencies": { "define-properties": "1.2.1", "es-abstract": "1.23.9" } }, "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w=="], + + "string.prototype.trim": ["string.prototype.trim@1.2.10", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "define-data-property": "1.1.4", "define-properties": "1.2.1", "es-abstract": "1.23.9", "es-object-atoms": "1.1.1", "has-property-descriptors": "1.0.2" } }, "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA=="], + + "string.prototype.trimend": ["string.prototype.trimend@1.0.9", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "define-properties": "1.2.1", "es-object-atoms": "1.1.1" } }, "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ=="], + + "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "1.0.8", "define-properties": "1.2.1", "es-object-atoms": "1.1.1" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="], + + "string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "5.1.2" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], + + "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "2.1.0", "character-entities-legacy": "3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], + + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], + + "strip-eof": ["strip-eof@1.0.0", "", {}, "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q=="], + + "strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="], + + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + + "strnum": ["strnum@2.4.2", "", { "dependencies": { "anynum": "^1.0.1" } }, "sha512-rDG3Ah4TV0k1hWvLSzkZtMmLN9+eS+h3knq4MP6A42Y3Yh5qGNnOUs1jJkoSr8FG5dsL28c7KgkIBzSEykqtuw=="], + + "style-to-js": ["style-to-js@1.1.16", "", { "dependencies": { "style-to-object": "1.0.8" } }, "sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw=="], + + "style-to-object": ["style-to-object@1.0.8", "", { "dependencies": { "inline-style-parser": "0.2.4" } }, "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g=="], + + "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "@babel/core": "*", "babel-plugin-macros": "*", "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" }, "optionalPeers": ["@babel/core", "babel-plugin-macros"] }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], + + "sucrase": ["sucrase@3.35.0", "", { "dependencies": { "@jridgewell/gen-mapping": "0.3.8", "commander": "4.1.1", "glob": "10.4.5", "lines-and-columns": "1.2.4", "mz": "2.7.0", "pirates": "4.0.7", "ts-interface-checker": "0.1.13" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA=="], + + "supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "swagger-autogen": ["swagger-autogen@2.23.7", "", { "dependencies": { "acorn": "7.4.1", "deepmerge": "4.3.1", "glob": "7.2.3", "json5": "2.2.3" } }, "sha512-vr7uRmuV0DCxWc0wokLJAwX3GwQFJ0jwN+AWk0hKxre2EZwusnkGSGdVFd82u7fQLgwSTnbWkxUL7HXuz5LTZQ=="], + + "swagger-ui-dist": ["swagger-ui-dist@5.31.0", "", { "dependencies": { "@scarf/scarf": "1.4.0" } }, "sha512-zSUTIck02fSga6rc0RZP3b7J7wgHXwLea8ZjgLA3Vgnb8QeOl3Wou2/j5QkzSGeoz6HusP/coYuJl33aQxQZpg=="], + + "swagger-ui-express": ["swagger-ui-express@5.0.1", "", { "dependencies": { "swagger-ui-dist": "5.31.0" }, "peerDependencies": { "express": ">=4.0.0 || >=5.0.0-beta" } }, "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA=="], + + "symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="], + + "tailwind-merge": ["tailwind-merge@2.6.0", "", {}, "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA=="], + + "tailwindcss": ["tailwindcss@4.1.4", "", {}, "sha512-1ZIUqtPITFbv/DxRmDr5/agPqJwF69d24m9qmM1939TJehgY539CtzeZRjbLt5G6fSy/7YqqYsfvoTEw9xUI2A=="], + + "tailwindcss-animate": ["tailwindcss-animate@1.0.7", "", { "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders" } }, "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA=="], + + "tapable": ["tapable@2.2.1", "", {}, "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="], + + "term-size": ["term-size@2.2.1", "", {}, "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg=="], + + "test-exclude": ["test-exclude@5.2.3", "", { "dependencies": { "glob": "7.2.3", "minimatch": "3.1.2", "read-pkg-up": "4.0.0", "require-main-filename": "2.0.0" } }, "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g=="], + + "text-table": ["text-table@0.2.0", "", {}, "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="], + + "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "1.3.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], + + "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": "3.3.1" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], + + "thread-stream": ["thread-stream@3.1.0", "", { "dependencies": { "real-require": "0.2.0" } }, "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A=="], + + "throat": ["throat@4.1.0", "", {}, "sha512-wCVxLDcFxw7ujDxaeJC6nfl2XfHJNYs8yUYJnvMgtPEFlttP9tHSfRUv2vBe6C4hkVFPWoP1P6ZccbYjmSEkKA=="], + + "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], + + "tinyglobby": ["tinyglobby@0.2.12", "", { "dependencies": { "fdir": "6.4.3", "picomatch": "4.0.2" } }, "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww=="], + + "tmp": ["tmp@0.2.7", "", {}, "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw=="], + + "tmpl": ["tmpl@1.0.5", "", {}, "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw=="], + + "to-object-path": ["to-object-path@0.3.0", "", { "dependencies": { "kind-of": "3.2.2" } }, "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg=="], + + "to-regex": ["to-regex@3.0.2", "", { "dependencies": { "define-property": "2.0.2", "extend-shallow": "3.0.2", "regex-not": "1.0.2", "safe-regex": "1.1.0" } }, "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + + "tough-cookie": ["tough-cookie@2.5.0", "", { "dependencies": { "psl": "1.15.0", "punycode": "2.3.1" } }, "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g=="], + + "tr46": ["tr46@1.0.1", "", { "dependencies": { "punycode": "2.3.1" } }, "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA=="], + + "tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="], + + "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], + + "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], + + "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], + + "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="], + + "tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "0.0.29", "json5": "1.0.2", "minimist": "1.2.8", "strip-bom": "3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "tsup": ["tsup@8.4.0", "", { "dependencies": { "bundle-require": "5.1.0", "cac": "6.7.14", "chokidar": "4.0.3", "consola": "3.4.2", "debug": "4.4.0", "esbuild": "0.25.2", "joycon": "3.1.1", "picocolors": "1.1.1", "postcss-load-config": "6.0.1", "resolve-from": "5.0.0", "rollup": "4.40.0", "source-map": "0.8.0-beta.0", "sucrase": "3.35.0", "tinyexec": "0.3.2", "tinyglobby": "0.2.12", "tree-kill": "1.2.2" }, "peerDependencies": { "@microsoft/api-extractor": "^7.36.0", "@swc/core": "^1", "postcss": "^8.4.12", "typescript": ">=4.5.0" }, "optionalPeers": ["@microsoft/api-extractor", "@swc/core", "postcss", "typescript"], "bin": { "tsup": "dist/cli-default.js", "tsup-node": "dist/cli-node.js" } }, "sha512-b+eZbPCjz10fRryaAA7C8xlIHnf8VnsaRqydheLIqwG/Mcpfk8Z5zp3HayX7GaTygkigHl5cBUs+IhcySiIexQ=="], + + "tsutils": ["tsutils@3.21.0", "", { "dependencies": { "tslib": "1.14.1" }, "peerDependencies": { "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA=="], + + "tsx": ["tsx@4.23.15", "", { "dependencies": { "esbuild": "~0.28.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw=="], + + "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], + + "tus-js-client": ["tus-js-client@4.3.1", "", { "dependencies": { "buffer-from": "1.1.2", "combine-errors": "3.0.3", "is-stream": "2.0.1", "js-base64": "3.7.8", "lodash.throttle": "4.1.1", "proper-lockfile": "4.1.2", "url-parse": "1.5.10" } }, "sha512-ZLeYmjrkaU1fUsKbIi8JML52uAocjEZtBx4DKjRrqzrZa0O4MYwT6db+oqePlspV+FxXJAyFBc/L5gwUi2OFsg=="], + + "tweetnacl": ["tweetnacl@0.14.5", "", {}, "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="], + + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + + "type-fest": ["type-fest@0.20.2", "", {}, "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="], + + "type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "2.1.35" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="], + + "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "1.0.4", "es-errors": "1.3.0", "is-typed-array": "1.1.15" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], + + "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "1.0.8", "for-each": "0.3.5", "gopd": "1.2.0", "has-proto": "1.2.0", "is-typed-array": "1.1.15" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="], + + "typed-array-byte-offset": ["typed-array-byte-offset@1.0.4", "", { "dependencies": { "available-typed-arrays": "1.0.7", "call-bind": "1.0.8", "for-each": "0.3.5", "gopd": "1.2.0", "has-proto": "1.2.0", "is-typed-array": "1.1.15", "reflect.getprototypeof": "1.0.10" } }, "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ=="], + + "typed-array-length": ["typed-array-length@1.0.7", "", { "dependencies": { "call-bind": "1.0.8", "for-each": "0.3.5", "gopd": "1.2.0", "is-typed-array": "1.1.15", "possible-typed-array-names": "1.1.0", "reflect.getprototypeof": "1.0.10" } }, "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg=="], + + "typedarray": ["typedarray@0.0.6", "", {}, "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "typescript-eslint": ["typescript-eslint@8.29.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.29.1", "@typescript-eslint/parser": "8.29.1", "@typescript-eslint/utils": "8.29.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-f8cDkvndhbQMPcysk6CUSGBWV+g1utqdn71P5YKwMumVMOG/5k7cHq0KyG4O52nB0oKS4aN2Tp5+wB4APJGC+w=="], + + "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "1.0.4", "has-bigints": "1.1.0", "has-symbols": "1.1.0", "which-boxed-primitive": "1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], + + "undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "3.0.3", "bail": "2.0.2", "devlop": "1.1.0", "extend": "3.0.2", "is-plain-obj": "4.1.0", "trough": "2.2.0", "vfile": "6.0.3" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], + + "union-value": ["union-value@1.0.1", "", { "dependencies": { "arr-union": "3.1.0", "get-value": "2.0.6", "is-extendable": "0.1.1", "set-value": "2.0.1" } }, "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg=="], + + "unist-util-is": ["unist-util-is@6.0.0", "", { "dependencies": { "@types/unist": "3.0.3" } }, "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw=="], + + "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "3.0.3" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], + + "unist-util-position-from-estree": ["unist-util-position-from-estree@2.0.0", "", { "dependencies": { "@types/unist": "3.0.3" } }, "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ=="], + + "unist-util-remove-position": ["unist-util-remove-position@5.0.0", "", { "dependencies": { "@types/unist": "3.0.3", "unist-util-visit": "5.1.0" } }, "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q=="], + + "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "3.0.3" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], + + "unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "3.0.3", "unist-util-is": "6.0.0", "unist-util-visit-parents": "6.0.1" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="], + + "unist-util-visit-parents": ["unist-util-visit-parents@6.0.1", "", { "dependencies": { "@types/unist": "3.0.3", "unist-util-is": "6.0.0" } }, "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw=="], + + "universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], + + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + + "unrs-resolver": ["unrs-resolver@1.4.1", "", { "optionalDependencies": { "@unrs/resolver-binding-darwin-arm64": "1.4.1", "@unrs/resolver-binding-darwin-x64": "1.4.1", "@unrs/resolver-binding-freebsd-x64": "1.4.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.4.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.4.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.4.1", "@unrs/resolver-binding-linux-arm64-musl": "1.4.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.4.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.4.1", "@unrs/resolver-binding-linux-x64-gnu": "1.4.1", "@unrs/resolver-binding-linux-x64-musl": "1.4.1", "@unrs/resolver-binding-wasm32-wasi": "1.4.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.4.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.4.1", "@unrs/resolver-binding-win32-x64-msvc": "1.4.1" } }, "sha512-MhPB3wBI5BR8TGieTb08XuYlE8oFVEXdSAgat3psdlRyejl8ojQ8iqPcjh094qCZ1r+TnkxzP6BeCd/umfHckQ=="], + + "unset-value": ["unset-value@1.0.0", "", { "dependencies": { "has-value": "0.3.1", "isobject": "3.0.1" } }, "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ=="], + + "update-browserslist-db": ["update-browserslist-db@1.3.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "2.3.1" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "urix": ["urix@0.1.0", "", {}, "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg=="], + + "url-parse": ["url-parse@1.5.10", "", { "dependencies": { "querystringify": "2.2.0", "requires-port": "1.0.0" } }, "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ=="], + + "use": ["use@3.1.1", "", {}, "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="], + + "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "2.8.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], + + "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "1.1.0", "tslib": "2.8.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "util.promisify": ["util.promisify@1.1.3", "", { "dependencies": { "call-bind": "1.0.8", "call-bound": "1.0.4", "define-data-property": "1.1.4", "define-properties": "1.2.1", "es-errors": "1.3.0", "es-object-atoms": "1.1.1", "for-each": "0.3.5", "get-intrinsic": "1.3.0", "has-proto": "1.2.0", "has-symbols": "1.1.0", "object.getownpropertydescriptors": "2.1.8", "safe-array-concat": "1.1.3" } }, "sha512-GIEaZ6o86fj09Wtf0VfZ5XP7tmd4t3jM5aZCgmBi231D0DB1AEBa3Aa6MP48DMsAIi96WkpWLimIWVwOjbDMOw=="], + + "utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="], + + "uuid": ["uuid@3.4.0", "", { "bin": { "uuid": "./bin/uuid" } }, "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="], + + "validate-npm-package-license": ["validate-npm-package-license@3.0.4", "", { "dependencies": { "spdx-correct": "3.2.0", "spdx-expression-parse": "3.0.1" } }, "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew=="], + + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + + "verror": ["verror@1.10.0", "", { "dependencies": { "assert-plus": "1.0.0", "core-util-is": "1.0.2", "extsprintf": "1.3.0" } }, "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw=="], + + "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "3.0.3", "vfile-message": "4.0.2" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], + + "vfile-location": ["vfile-location@5.0.3", "", { "dependencies": { "@types/unist": "3.0.3", "vfile": "6.0.3" } }, "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg=="], + + "vfile-message": ["vfile-message@4.0.2", "", { "dependencies": { "@types/unist": "3.0.3", "unist-util-stringify-position": "4.0.0" } }, "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw=="], + + "vite": ["vite@7.3.6", "", { "dependencies": { "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "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" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg=="], + + "w3c-hr-time": ["w3c-hr-time@1.0.2", "", { "dependencies": { "browser-process-hrtime": "1.0.0" } }, "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ=="], + + "walker": ["walker@1.0.8", "", { "dependencies": { "makeerror": "1.0.12" } }, "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ=="], + + "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], + + "web-vitals": ["web-vitals@6.2.2", "", {}, "sha512-oto5x6dLEgrRqfcWed+pZEUb2q6ikbFmqF54CRDhI/QGbn+qxC49b4C4OkbH+kb9C3a8shpFD3SgR9kRcs80ZQ=="], + + "web-vitals-soft-navs": ["web-vitals@6.2.1", "", {}, "sha512-rLcLXA2sx6+9dE88NHFubwTtGxpK4yYBLj6qHPdFoCaLr0cXGb4efOqtKLlm4loGA4OEKHIQKMKzZkKyOh5ctw=="], + + "webidl-conversions": ["webidl-conversions@4.0.2", "", {}, "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="], + + "whatwg-encoding": ["whatwg-encoding@1.0.5", "", { "dependencies": { "iconv-lite": "0.4.24" } }, "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw=="], + + "whatwg-mimetype": ["whatwg-mimetype@2.3.0", "", {}, "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g=="], + + "whatwg-url": ["whatwg-url@7.1.0", "", { "dependencies": { "lodash.sortby": "4.7.0", "tr46": "1.0.1", "webidl-conversions": "4.0.2" } }, "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "1.1.0", "is-boolean-object": "1.2.2", "is-number-object": "1.1.1", "is-string": "1.1.1", "is-symbol": "1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="], + + "which-builtin-type": ["which-builtin-type@1.2.1", "", { "dependencies": { "call-bound": "1.0.4", "function.prototype.name": "1.1.8", "has-tostringtag": "1.0.2", "is-async-function": "2.1.1", "is-date-object": "1.1.0", "is-finalizationregistry": "1.1.1", "is-generator-function": "1.1.0", "is-regex": "1.2.1", "is-weakref": "1.1.1", "isarray": "2.0.5", "which-boxed-primitive": "1.1.1", "which-collection": "1.0.2", "which-typed-array": "1.1.19" } }, "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q=="], + + "which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "2.0.3", "is-set": "2.0.3", "is-weakmap": "2.0.2", "is-weakset": "2.0.4" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="], + + "which-module": ["which-module@2.0.1", "", {}, "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="], + + "which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "1.0.7", "call-bind": "1.0.8", "call-bound": "1.0.4", "for-each": "0.3.5", "get-proto": "1.0.1", "gopd": "1.2.0", "has-tostringtag": "1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="], + + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + + "wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "6.2.1", "string-width": "7.2.0", "strip-ansi": "7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="], + + "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "4.3.0", "string-width": "4.2.3", "strip-ansi": "6.0.1" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + + "write-file-atomic": ["write-file-atomic@2.4.1", "", { "dependencies": { "graceful-fs": "4.2.11", "imurmurhash": "0.1.4", "signal-exit": "3.0.7" } }, "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg=="], + + "ws": ["ws@5.2.5", "", { "dependencies": { "async-limiter": "~1.0.0" } }, "sha512-G0gACQIjFmv7NqpaOAXEpe9nEtRYD6ZCy2Ip/B4EzR08qEwnf/wYJoQd3cjosPdnJsHkeh0j2tzOaIBZIOC1yA=="], + + "xml-js": ["xml-js@1.6.11", "", { "dependencies": { "sax": "1.4.1" }, "bin": { "xml-js": "./bin/cli.js" } }, "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g=="], + + "xml-name-validator": ["xml-name-validator@3.0.0", "", {}, "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw=="], + + "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], + + "y18n": ["y18n@4.0.3", "", {}, "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="], + + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + + "yaml": ["yaml@2.8.3", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg=="], + + "yargs": ["yargs@13.3.2", "", { "dependencies": { "cliui": "5.0.0", "find-up": "3.0.0", "get-caller-file": "2.0.5", "require-directory": "2.1.1", "require-main-filename": "2.0.0", "set-blocking": "2.0.0", "string-width": "3.1.0", "which-module": "2.0.1", "y18n": "4.0.3", "yargs-parser": "13.1.2" } }, "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw=="], + + "yargs-parser": ["yargs-parser@13.1.2", "", { "dependencies": { "camelcase": "5.3.1", "decamelize": "1.2.0" } }, "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg=="], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], + + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + + "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@babel/generator/@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@babel/generator/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@better-auth/core/zod": ["zod@4.6.5", "", {}, "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q=="], + + "@better-auth/oauth-provider/zod": ["zod@4.6.5", "", {}, "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q=="], + + "@changesets/apply-release-plan/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], + + "@changesets/parse/js-yaml": ["js-yaml@3.15.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w=="], + + "@changesets/write/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], + + "@codelitdev/billing/nanoid": ["nanoid@5.1.16", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ=="], + + "@esbuild-kit/core-utils/esbuild": ["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" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], + + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], + + "@example/next-app-router/eslint-config-next": ["eslint-config-next@15.3.1", "", { "dependencies": { "@next/eslint-plugin-next": "15.3.1", "@rushstack/eslint-patch": "1.11.0", "@typescript-eslint/eslint-plugin": "8.29.1", "@typescript-eslint/parser": "5.62.0", "eslint-import-resolver-node": "0.3.9", "eslint-import-resolver-typescript": "3.10.0", "eslint-plugin-import": "2.31.0", "eslint-plugin-jsx-a11y": "6.10.2", "eslint-plugin-react": "7.37.5", "eslint-plugin-react-hooks": "5.2.0" }, "peerDependencies": { "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", "typescript": ">=3.3.1" }, "optionalPeers": ["typescript"] }, "sha512-GnmyVd9TE/Ihe3RrvcafFhXErErtr2jS0JDeCSp3vWvy86AXwHsRBt0E3MqP/m8ACS1ivcsi5uaqjbhsG18qKw=="], + + "@fumadocs/tailwind/postcss-selector-parser": ["postcss-selector-parser@7.1.3", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-ajnd7iZnqjJDkyHNfznl/ZVO0lWqvBmQXfKKENx9/p/bEiF/L3eHwdydNUg9RXZx6xfZWOCmXmBa5oeB+YrAPQ=="], + + "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "0.2.0", "emoji-regex": "9.2.2", "strip-ansi": "7.1.0" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + + "@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "6.1.0" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + + "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "6.2.1", "string-width": "5.1.2", "strip-ansi": "7.1.0" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + + "@jest/console/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", "supports-color": "5.5.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + + "@jest/console/slash": ["slash@2.0.0", "", {}, "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="], + + "@jest/core/ansi-escapes": ["ansi-escapes@3.2.0", "", {}, "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ=="], + + "@jest/core/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", "supports-color": "5.5.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + + "@jest/core/micromatch": ["micromatch@3.1.10", "", { "dependencies": { "arr-diff": "4.0.0", "array-unique": "0.3.2", "braces": "2.3.2", "define-property": "2.0.2", "extend-shallow": "3.0.2", "extglob": "2.0.4", "fragment-cache": "0.2.1", "kind-of": "6.0.3", "nanomatch": "1.2.13", "object.pick": "1.3.0", "regex-not": "1.0.2", "snapdragon": "0.8.2", "to-regex": "3.0.2" } }, "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg=="], + + "@jest/core/slash": ["slash@2.0.0", "", {}, "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="], + + "@jest/core/strip-ansi": ["strip-ansi@5.2.0", "", { "dependencies": { "ansi-regex": "4.1.1" } }, "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA=="], + + "@jest/reporters/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", "supports-color": "5.5.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + + "@jest/reporters/slash": ["slash@2.0.0", "", {}, "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="], + + "@jest/reporters/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + + "@jest/source-map/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + + "@jest/transform/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", "supports-color": "5.5.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + + "@jest/transform/convert-source-map": ["convert-source-map@1.9.0", "", {}, "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="], + + "@jest/transform/micromatch": ["micromatch@3.1.10", "", { "dependencies": { "arr-diff": "4.0.0", "array-unique": "0.3.2", "braces": "2.3.2", "define-property": "2.0.2", "extend-shallow": "3.0.2", "extglob": "2.0.4", "fragment-cache": "0.2.1", "kind-of": "6.0.3", "nanomatch": "1.2.13", "object.pick": "1.3.0", "regex-not": "1.0.2", "snapdragon": "0.8.2", "to-regex": "3.0.2" } }, "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg=="], + + "@jest/transform/slash": ["slash@2.0.0", "", {}, "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="], + + "@jest/transform/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + + "@jridgewell/remapping/@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@manypkg/find-root/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "@manypkg/find-root/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "5.0.0", "path-exists": "4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], + + "@manypkg/find-root/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "4.2.11", "jsonfile": "4.0.0", "universalify": "0.1.2" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], + + "@manypkg/get-packages/@changesets/types": ["@changesets/types@4.1.0", "", {}, "sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw=="], + + "@manypkg/get-packages/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "4.2.11", "jsonfile": "4.0.0", "universalify": "0.1.2" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], + + "@mdx-js/mdx/@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], + + "@mdx-js/mdx/acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="], + + "@mdx-js/mdx/source-map": ["source-map@0.7.4", "", {}, "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA=="], + + "@medialit/api/@types/node": ["@types/node@22.14.1", "", { "dependencies": { "undici-types": "6.21.0" } }, "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw=="], + + "@medialit/docs/@types/node": ["@types/node@22.14.1", "", { "dependencies": { "undici-types": "6.21.0" } }, "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw=="], + + "@medialit/docs/lucide-react": ["lucide-react@0.577.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A=="], + + "@medialit/scripts/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + + "@medialit/web/eslint": ["eslint@8.57.1", "", { "dependencies": { "@eslint-community/eslint-utils": "4.5.1", "@eslint-community/regexpp": "4.12.1", "@eslint/eslintrc": "2.1.4", "@eslint/js": "8.57.1", "@humanwhocodes/config-array": "0.13.0", "@humanwhocodes/module-importer": "1.0.1", "@nodelib/fs.walk": "1.2.8", "@ungap/structured-clone": "1.3.0", "ajv": "6.12.6", "chalk": "4.1.2", "cross-spawn": "7.0.6", "debug": "4.4.0", "doctrine": "3.0.0", "escape-string-regexp": "4.0.0", "eslint-scope": "7.2.2", "eslint-visitor-keys": "3.4.3", "espree": "9.6.1", "esquery": "1.6.0", "esutils": "2.0.3", "fast-deep-equal": "3.1.3", "file-entry-cache": "6.0.1", "find-up": "5.0.0", "glob-parent": "6.0.2", "globals": "13.24.0", "graphemer": "1.4.0", "ignore": "5.3.2", "imurmurhash": "0.1.4", "is-glob": "4.0.3", "is-path-inside": "3.0.3", "js-yaml": "4.1.0", "json-stable-stringify-without-jsonify": "1.0.1", "levn": "0.4.1", "lodash.merge": "4.6.2", "minimatch": "3.1.2", "natural-compare": "1.4.0", "optionator": "0.9.4", "strip-ansi": "6.0.1", "text-table": "0.2.0" }, "bin": { "eslint": "bin/eslint.js" } }, "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA=="], + + "@medialit/web/tailwindcss": ["tailwindcss@3.4.17", "", { "dependencies": { "@alloc/quick-lru": "5.2.0", "arg": "5.0.2", "chokidar": "3.6.0", "didyoumean": "1.2.2", "dlv": "1.1.3", "fast-glob": "3.3.3", "glob-parent": "6.0.2", "is-glob": "4.0.3", "jiti": "1.21.7", "lilconfig": "3.1.3", "micromatch": "4.0.8", "normalize-path": "3.0.0", "object-hash": "3.0.0", "picocolors": "1.1.1", "postcss": "8.5.3", "postcss-import": "15.1.0", "postcss-js": "4.0.1", "postcss-load-config": "4.0.2", "postcss-nested": "6.2.0", "postcss-selector-parser": "6.1.2", "resolve": "1.22.10", "sucrase": "3.35.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og=="], + + "@modelcontextprotocol/sdk/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "3.1.3", "fast-uri": "3.1.0", "json-schema-traverse": "1.0.0", "require-from-string": "2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + + "@modelcontextprotocol/sdk/express": ["express@5.2.1", "", { "dependencies": { "accepts": "2.0.0", "body-parser": "2.2.2", "content-disposition": "1.1.0", "content-type": "1.0.5", "cookie": "0.7.1", "cookie-signature": "1.2.2", "debug": "4.4.3", "depd": "2.0.0", "encodeurl": "2.0.0", "escape-html": "1.0.3", "etag": "1.8.1", "finalhandler": "2.1.1", "fresh": "2.0.0", "http-errors": "2.0.0", "merge-descriptors": "2.0.0", "mime-types": "3.0.2", "on-finished": "2.4.1", "once": "1.4.0", "parseurl": "1.3.3", "proxy-addr": "2.0.7", "qs": "6.15.2", "range-parser": "1.2.1", "router": "2.2.0", "send": "1.2.1", "serve-static": "2.2.1", "statuses": "2.0.1", "type-is": "2.1.0", "vary": "1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], + + "@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@8.5.2", "", { "dependencies": { "ip-address": "10.2.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A=="], + + "@modelcontextprotocol/sdk/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + + "@napi-rs/wasm-runtime/@emnapi/runtime": ["@emnapi/runtime@1.7.1", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA=="], + + "@radix-ui/react-accordion/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-accordion/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-accordion/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-arrow/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-collapsible/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-collapsible/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-collapsible/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-collection/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="], + + "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="], + + "@radix-ui/react-dialog/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-dismissable-layer/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-dismissable-layer/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-dropdown-menu/@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="], + + "@radix-ui/react-dropdown-menu/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="], + + "@radix-ui/react-dropdown-menu/@radix-ui/react-id": ["@radix-ui/react-id@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA=="], + + "@radix-ui/react-dropdown-menu/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw=="], + + "@radix-ui/react-focus-scope/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-focus-scope/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-menu/@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="], + + "@radix-ui/react-menu/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="], + + "@radix-ui/react-menu/@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg=="], + + "@radix-ui/react-menu/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.5", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-escape-keydown": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg=="], + + "@radix-ui/react-menu/@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg=="], + + "@radix-ui/react-menu/@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA=="], + + "@radix-ui/react-menu/@radix-ui/react-id": ["@radix-ui/react-id@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA=="], + + "@radix-ui/react-menu/@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.2", "", { "dependencies": { "@floating-ui/react-dom": "2.1.2", "@radix-ui/react-arrow": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0", "@radix-ui/react-use-rect": "1.1.0", "@radix-ui/react-use-size": "1.1.0", "@radix-ui/rect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA=="], + + "@radix-ui/react-menu/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.4", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA=="], + + "@radix-ui/react-menu/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg=="], + + "@radix-ui/react-menu/@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.2", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw=="], + + "@radix-ui/react-menu/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="], + + "@radix-ui/react-menu/react-remove-scroll": ["react-remove-scroll@2.6.3", "", { "dependencies": { "react-remove-scroll-bar": "2.3.8", "react-style-singleton": "2.2.3", "tslib": "2.8.1", "use-callback-ref": "1.3.3", "use-sidecar": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ=="], + + "@radix-ui/react-navigation-menu/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-navigation-menu/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-navigation-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-navigation-menu/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-navigation-menu/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-navigation-menu/@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], + + "@radix-ui/react-navigation-menu/@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="], + + "@radix-ui/react-popover/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-popover/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-popover/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-popper/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-popper/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-popper/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-popper/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-popper/@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="], + + "@radix-ui/react-portal/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-portal/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="], + + "@radix-ui/react-progress/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-progress/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.0", "", { "dependencies": { "@radix-ui/react-slot": "1.2.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw=="], + + "@radix-ui/react-roving-focus/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-roving-focus/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-roving-focus/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-roving-focus/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-scroll-area/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-scroll-area/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-scroll-area/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-scroll-area/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-select/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-select/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-select/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-select/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-select/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-select/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-select/@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], + + "@radix-ui/react-select/@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="], + + "@radix-ui/react-select/react-remove-scroll": ["react-remove-scroll@2.6.3", "", { "dependencies": { "react-remove-scroll-bar": "2.3.8", "react-style-singleton": "2.2.3", "tslib": "2.8.1", "use-callback-ref": "1.3.3", "use-sidecar": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ=="], + + "@radix-ui/react-switch/@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="], + + "@radix-ui/react-switch/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="], + + "@radix-ui/react-switch/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw=="], + + "@radix-ui/react-tabs/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-tabs/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-toast/@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="], + + "@radix-ui/react-toast/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="], + + "@radix-ui/react-toast/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.5", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-escape-keydown": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg=="], + + "@radix-ui/react-toast/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.4", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA=="], + + "@radix-ui/react-toast/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg=="], + + "@radix-ui/react-toast/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw=="], + + "@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-use-effect-event/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-use-escape-keydown/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@scalar/openapi-types/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + + "@tailwindcss/node/jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="], + + "@types/babel__core/@babel/parser": ["@babel/parser@7.27.0", "", { "dependencies": { "@babel/types": "7.27.0" }, "bin": "./bin/babel-parser.js" }, "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg=="], + + "@types/babel__core/@babel/types": ["@babel/types@7.27.0", "", { "dependencies": { "@babel/helper-string-parser": "7.25.9", "@babel/helper-validator-identifier": "7.25.9" } }, "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg=="], + + "@types/babel__generator/@babel/types": ["@babel/types@7.27.0", "", { "dependencies": { "@babel/helper-string-parser": "7.25.9", "@babel/helper-validator-identifier": "7.25.9" } }, "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg=="], + + "@types/babel__template/@babel/parser": ["@babel/parser@7.27.0", "", { "dependencies": { "@babel/types": "7.27.0" }, "bin": "./bin/babel-parser.js" }, "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg=="], + + "@types/babel__template/@babel/types": ["@babel/types@7.27.0", "", { "dependencies": { "@babel/helper-string-parser": "7.25.9", "@babel/helper-validator-identifier": "7.25.9" } }, "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg=="], + + "@types/babel__traverse/@babel/types": ["@babel/types@7.27.0", "", { "dependencies": { "@babel/helper-string-parser": "7.25.9", "@babel/helper-validator-identifier": "7.25.9" } }, "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg=="], + + "@types/estree-jsx/@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], + + "@types/pg/@types/node": ["@types/node@22.14.1", "", { "dependencies": { "undici-types": "6.21.0" } }, "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw=="], + + "@typescript-eslint/parser/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "@typescript-eslint/type-utils/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "@typescript-eslint/typescript-estree/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "@typescript-eslint/typescript-estree/fast-glob": ["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" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.7", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-MOwgjc8tfrpn5QQEvjijjmDVtMw2oL88ugTevzxQnzRLm6l3fVEF2gzU0kYeYYKD8C66+IdGX6peJ4MyUlUnPg=="], + + "@typescript-eslint/typescript-estree/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + + "@typescript-eslint/utils/@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.5.1", "", { "dependencies": { "eslint-visitor-keys": "3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w=="], + + "@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@4.2.0", "", {}, "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw=="], + + "accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], + + "acorn-globals/acorn": ["acorn@6.4.2", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ=="], + + "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + + "babel-jest/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", "supports-color": "5.5.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + + "babel-jest/slash": ["slash@2.0.0", "", {}, "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="], + + "babel-plugin-istanbul/find-up": ["find-up@3.0.0", "", { "dependencies": { "locate-path": "3.0.0" } }, "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg=="], + + "base/define-property": ["define-property@1.0.0", "", { "dependencies": { "is-descriptor": "1.0.3" } }, "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA=="], + + "better-auth/zod": ["zod@4.6.5", "", {}, "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q=="], + + "better-call/@better-auth/utils": ["@better-auth/utils@0.5.0", "", { "dependencies": { "@noble/hashes": "^2.0.1" } }, "sha512-BL8W4EfIZFwlu0r54m3v1ztjDhu6dDe/amLTm0xybmbZaNgYUqhD3SjpAsnq0q8YD6/ki4iwIgxJNLP/N3TxiA=="], + + "better-call/@better-fetch/fetch": ["@better-fetch/fetch@1.3.1", "", {}, "sha512-ABkD1WhyfPZprKRQI3bhATjeiFuNWC9PXhfGWqL+sg/gKrM977oFrYkdb4msM3hgUGonr7KlOsOFT5TU2rht9g=="], + + "better-call/set-cookie-parser": ["set-cookie-parser@3.1.2", "", {}, "sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw=="], + + "body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + + "body-parser/http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.2", "toidentifier": "1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + + "body-parser/raw-body": ["raw-body@2.5.3", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "unpipe": "~1.0.0" } }, "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA=="], + + "browser-resolve/resolve": ["resolve@1.1.7", "", {}, "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg=="], + + "browserslist/caniuse-lite": ["caniuse-lite@1.0.30001814", "", {}, "sha512-/Uaf1lAzr59XcMpW0o96WoEfr+VXK2OX4U9AgFoiSHsVJ4HppnIFUjtYzsyDH2+tgANaQb2/oxYGwCPapN1FpA=="], + + "chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "class-utils/define-property": ["define-property@0.2.5", "", { "dependencies": { "is-descriptor": "0.1.7" } }, "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA=="], + + "cliui/string-width": ["string-width@3.1.0", "", { "dependencies": { "emoji-regex": "7.0.3", "is-fullwidth-code-point": "2.0.0", "strip-ansi": "5.2.0" } }, "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w=="], + + "cliui/strip-ansi": ["strip-ansi@5.2.0", "", { "dependencies": { "ansi-regex": "4.1.1" } }, "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA=="], + + "cliui/wrap-ansi": ["wrap-ansi@5.1.0", "", { "dependencies": { "ansi-styles": "3.2.1", "string-width": "3.1.0", "strip-ansi": "5.2.0" } }, "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q=="], + + "esast-util-from-js/acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="], + + "escodegen/estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="], + + "escodegen/optionator": ["optionator@0.8.3", "", { "dependencies": { "deep-is": "0.1.4", "fast-levenshtein": "2.0.6", "levn": "0.3.0", "prelude-ls": "1.1.2", "type-check": "0.3.2", "word-wrap": "1.2.5" } }, "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA=="], + + "escodegen/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + + "eslint/@eslint/js": ["@eslint/js@9.39.5", "", {}, "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A=="], + + "eslint-config-next/@typescript-eslint/parser": ["@typescript-eslint/parser@5.62.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", "@typescript-eslint/typescript-estree": "5.62.0", "debug": "4.4.0" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0", "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA=="], + + "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-import-resolver-node/resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "2.16.1", "path-parse": "1.0.7", "supports-preserve-symlinks-flag": "1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], + + "eslint-import-resolver-typescript/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "6.5.0", "picomatch": "4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-plugin-import/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "eslint-plugin-react/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "espree/acorn": ["acorn@8.18.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ=="], + + "estree-util-attach-comments/@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], + + "estree-util-scope/@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], + + "estree-util-to-js/source-map": ["source-map@0.7.4", "", {}, "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA=="], + + "estree-util-value-to-estree/@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], + + "estree-walker/@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], + + "execa/is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="], + + "expand-brackets/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + + "expand-brackets/define-property": ["define-property@0.2.5", "", { "dependencies": { "is-descriptor": "0.1.7" } }, "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA=="], + + "expand-brackets/extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "0.1.1" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], + + "expect/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "express/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + + "extglob/define-property": ["define-property@1.0.0", "", { "dependencies": { "is-descriptor": "1.0.3" } }, "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA=="], + + "extglob/extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "0.1.1" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], + + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "4.0.3" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + + "form-data/hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], + + "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "fumadocs-core/path-to-regexp": ["path-to-regexp@8.4.0", "", {}, "sha512-PuseHIvAnz3bjrM2rGJtSgo1zjgxapTLZ7x2pjhzWwlp4SJQgK3f3iZIQwkpEnBaKz6seKBADpM4B4ySkuYypg=="], + + "fumadocs-core/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "6.5.0", "picomatch": "4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "fumadocs-core/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + + "fumadocs-mdx/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], + + "fumadocs-mdx/esbuild": ["esbuild@0.27.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.4", "@esbuild/android-arm": "0.27.4", "@esbuild/android-arm64": "0.27.4", "@esbuild/android-x64": "0.27.4", "@esbuild/darwin-arm64": "0.27.4", "@esbuild/darwin-x64": "0.27.4", "@esbuild/freebsd-arm64": "0.27.4", "@esbuild/freebsd-x64": "0.27.4", "@esbuild/linux-arm": "0.27.4", "@esbuild/linux-arm64": "0.27.4", "@esbuild/linux-ia32": "0.27.4", "@esbuild/linux-loong64": "0.27.4", "@esbuild/linux-mips64el": "0.27.4", "@esbuild/linux-ppc64": "0.27.4", "@esbuild/linux-riscv64": "0.27.4", "@esbuild/linux-s390x": "0.27.4", "@esbuild/linux-x64": "0.27.4", "@esbuild/netbsd-arm64": "0.27.4", "@esbuild/netbsd-x64": "0.27.4", "@esbuild/openbsd-arm64": "0.27.4", "@esbuild/openbsd-x64": "0.27.4", "@esbuild/openharmony-arm64": "0.27.4", "@esbuild/sunos-x64": "0.27.4", "@esbuild/win32-arm64": "0.27.4", "@esbuild/win32-ia32": "0.27.4", "@esbuild/win32-x64": "0.27.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ=="], + + "fumadocs-mdx/tinyexec": ["tinyexec@1.0.4", "", {}, "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw=="], + + "fumadocs-mdx/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "6.5.0", "picomatch": "4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "fumadocs-mdx/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + + "fumadocs-openapi/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "3.1.3", "fast-uri": "3.1.0", "json-schema-traverse": "1.0.0", "require-from-string": "2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + + "fumadocs-openapi/lucide-react": ["lucide-react@0.577.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A=="], + + "fumadocs-openapi/next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], + + "fumadocs-openapi/tailwind-merge": ["tailwind-merge@3.5.0", "", {}, "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A=="], + + "fumadocs-ui/lucide-react": ["lucide-react@0.577.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A=="], + + "fumadocs-ui/next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], + + "fumadocs-ui/tailwind-merge": ["tailwind-merge@3.5.0", "", {}, "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A=="], + + "globby/fast-glob": ["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" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "has-values/is-number": ["is-number@3.0.0", "", { "dependencies": { "kind-of": "3.2.2" } }, "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg=="], + + "has-values/kind-of": ["kind-of@4.0.0", "", { "dependencies": { "is-buffer": "1.1.6" } }, "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw=="], + + "hast-util-to-estree/@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], + + "hast-util-to-jsx-runtime/@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], + + "import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "ioredis/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "is-bun-module/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + + "is-ci/ci-info": ["ci-info@2.0.0", "", {}, "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="], + + "istanbul-lib-instrument/@babel/generator": ["@babel/generator@7.27.0", "", { "dependencies": { "@babel/parser": "7.27.0", "@babel/types": "7.27.0", "@jridgewell/gen-mapping": "0.3.8", "@jridgewell/trace-mapping": "0.3.25", "jsesc": "3.1.0" } }, "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw=="], + + "istanbul-lib-instrument/@babel/parser": ["@babel/parser@7.27.0", "", { "dependencies": { "@babel/types": "7.27.0" }, "bin": "./bin/babel-parser.js" }, "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg=="], + + "istanbul-lib-instrument/@babel/template": ["@babel/template@7.27.0", "", { "dependencies": { "@babel/code-frame": "7.26.2", "@babel/parser": "7.27.0", "@babel/types": "7.27.0" } }, "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA=="], + + "istanbul-lib-instrument/@babel/traverse": ["@babel/traverse@7.27.0", "", { "dependencies": { "@babel/code-frame": "7.26.2", "@babel/generator": "7.27.0", "@babel/parser": "7.27.0", "@babel/template": "7.27.0", "@babel/types": "7.27.0", "debug": "4.4.3", "globals": "11.12.0" } }, "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA=="], + + "istanbul-lib-instrument/@babel/types": ["@babel/types@7.27.0", "", { "dependencies": { "@babel/helper-string-parser": "7.25.9", "@babel/helper-validator-identifier": "7.25.9" } }, "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg=="], + + "istanbul-lib-instrument/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "istanbul-lib-report/supports-color": ["supports-color@6.1.0", "", { "dependencies": { "has-flag": "3.0.0" } }, "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ=="], + + "istanbul-lib-source-maps/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + + "jest-changed-files/execa": ["execa@1.0.0", "", { "dependencies": { "cross-spawn": "6.0.6", "get-stream": "4.1.0", "is-stream": "1.1.0", "npm-run-path": "2.0.2", "p-finally": "1.0.0", "signal-exit": "3.0.7", "strip-eof": "1.0.0" } }, "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA=="], + + "jest-cli/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", "supports-color": "5.5.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + + "jest-config/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", "supports-color": "5.5.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + + "jest-config/micromatch": ["micromatch@3.1.10", "", { "dependencies": { "arr-diff": "4.0.0", "array-unique": "0.3.2", "braces": "2.3.2", "define-property": "2.0.2", "extend-shallow": "3.0.2", "extglob": "2.0.4", "fragment-cache": "0.2.1", "kind-of": "6.0.3", "nanomatch": "1.2.13", "object.pick": "1.3.0", "regex-not": "1.0.2", "snapdragon": "0.8.2", "to-regex": "3.0.2" } }, "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg=="], + + "jest-diff/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", "supports-color": "5.5.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + + "jest-each/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", "supports-color": "5.5.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + + "jest-haste-map/anymatch": ["anymatch@2.0.0", "", { "dependencies": { "micromatch": "3.1.10", "normalize-path": "2.1.1" } }, "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw=="], + + "jest-haste-map/fsevents": ["fsevents@1.2.13", "", { "dependencies": { "bindings": "1.5.0", "nan": "2.22.2" }, "os": "darwin" }, "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw=="], + + "jest-haste-map/micromatch": ["micromatch@3.1.10", "", { "dependencies": { "arr-diff": "4.0.0", "array-unique": "0.3.2", "braces": "2.3.2", "define-property": "2.0.2", "extend-shallow": "3.0.2", "extglob": "2.0.4", "fragment-cache": "0.2.1", "kind-of": "6.0.3", "nanomatch": "1.2.13", "object.pick": "1.3.0", "regex-not": "1.0.2", "snapdragon": "0.8.2", "to-regex": "3.0.2" } }, "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg=="], + + "jest-jasmine2/@babel/traverse": ["@babel/traverse@7.27.0", "", { "dependencies": { "@babel/code-frame": "7.26.2", "@babel/generator": "7.27.0", "@babel/parser": "7.27.0", "@babel/template": "7.27.0", "@babel/types": "7.27.0", "debug": "4.4.3", "globals": "11.12.0" } }, "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA=="], + + "jest-jasmine2/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", "supports-color": "5.5.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + + "jest-matcher-utils/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", "supports-color": "5.5.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + + "jest-message-util/@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "7.25.9", "js-tokens": "4.0.0", "picocolors": "1.1.1" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="], + + "jest-message-util/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", "supports-color": "5.5.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + + "jest-message-util/micromatch": ["micromatch@3.1.10", "", { "dependencies": { "arr-diff": "4.0.0", "array-unique": "0.3.2", "braces": "2.3.2", "define-property": "2.0.2", "extend-shallow": "3.0.2", "extglob": "2.0.4", "fragment-cache": "0.2.1", "kind-of": "6.0.3", "nanomatch": "1.2.13", "object.pick": "1.3.0", "regex-not": "1.0.2", "snapdragon": "0.8.2", "to-regex": "3.0.2" } }, "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg=="], + + "jest-message-util/slash": ["slash@2.0.0", "", {}, "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="], + + "jest-resolve/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", "supports-color": "5.5.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + + "jest-runner/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", "supports-color": "5.5.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + + "jest-runtime/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", "supports-color": "5.5.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + + "jest-runtime/slash": ["slash@2.0.0", "", {}, "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="], + + "jest-snapshot/@babel/types": ["@babel/types@7.27.0", "", { "dependencies": { "@babel/helper-string-parser": "7.25.9", "@babel/helper-validator-identifier": "7.25.9" } }, "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg=="], + + "jest-snapshot/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", "supports-color": "5.5.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + + "jest-snapshot/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "jest-util/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", "supports-color": "5.5.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + + "jest-util/slash": ["slash@2.0.0", "", {}, "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="], + + "jest-util/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + + "jest-validate/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", "supports-color": "5.5.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + + "jest-watcher/ansi-escapes": ["ansi-escapes@3.2.0", "", {}, "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ=="], + + "jest-watcher/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", "supports-color": "5.5.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + + "jest-worker/supports-color": ["supports-color@6.1.0", "", { "dependencies": { "has-flag": "3.0.0" } }, "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ=="], + + "jsdom/acorn": ["acorn@5.7.4", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg=="], + + "jsdom/parse5": ["parse5@4.0.0", "", {}, "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA=="], + + "jsdom/whatwg-url": ["whatwg-url@6.5.0", "", { "dependencies": { "lodash.sortby": "4.7.0", "tr46": "1.0.1", "webidl-conversions": "4.0.2" } }, "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ=="], + + "jsonwebtoken/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + + "lightningcss/detect-libc": ["detect-libc@2.0.3", "", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="], + + "lint-staged/chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], + + "lint-staged/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "load-json-file/pify": ["pify@3.0.0", "", {}, "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="], + + "log-update/slice-ansi": ["slice-ansi@7.1.0", "", { "dependencies": { "ansi-styles": "6.2.1", "is-fullwidth-code-point": "5.0.0" } }, "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg=="], + + "log-update/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "6.1.0" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + + "make-dir/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], + + "mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + + "micromark-extension-mdx-expression/@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], + + "micromark-extension-mdx-jsx/@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], + + "micromark-extension-mdxjs/acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="], + + "micromark-extension-mdxjs-esm/@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], + + "micromark-factory-mdx-expression/@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], + + "micromark-util-events-to-acorn/@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], + + "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + + "mongodb-connection-string-url/whatwg-url": ["whatwg-url@14.2.0", "", { "dependencies": { "tr46": "5.1.0", "webidl-conversions": "7.0.0" } }, "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw=="], + + "node-notifier/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], + + "node-notifier/which": ["which@1.3.1", "", { "dependencies": { "isexe": "2.0.0" }, "bin": { "which": "./bin/which" } }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="], + + "normalize-package-data/resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "2.16.1", "path-parse": "1.0.7", "supports-preserve-symlinks-flag": "1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], + + "normalize-package-data/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], + + "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], + + "object-copy/define-property": ["define-property@0.2.5", "", { "dependencies": { "is-descriptor": "0.1.7" } }, "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA=="], + + "object-copy/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "1.1.6" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="], + + "p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], + + "path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "pkg-dir/find-up": ["find-up@3.0.0", "", { "dependencies": { "locate-path": "3.0.0" } }, "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg=="], + + "postcss-import/resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "2.16.1", "path-parse": "1.0.7", "supports-preserve-symlinks-flag": "1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], + + "pretty-format/ansi-regex": ["ansi-regex@4.1.1", "", {}, "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="], + + "pretty-format/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "proper-lockfile/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "qs/side-channel": ["side-channel@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ=="], + + "raw-body/http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.2", "toidentifier": "1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + + "raw-body/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": "2.1.2" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + + "read-cache/pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="], + + "read-pkg/path-type": ["path-type@3.0.0", "", { "dependencies": { "pify": "3.0.0" } }, "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg=="], + + "read-pkg-up/find-up": ["find-up@3.0.0", "", { "dependencies": { "locate-path": "3.0.0" } }, "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg=="], + + "read-yaml-file/js-yaml": ["js-yaml@3.15.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w=="], + + "readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], + + "readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + + "recma-build-jsx/@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], + + "recma-parse/@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], + + "recma-stringify/@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], + + "rehype-recma/@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], + + "request/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "resolve-cwd/resolve-from": ["resolve-from@3.0.0", "", {}, "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw=="], + + "restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "5.0.1" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], + + "router/path-to-regexp": ["path-to-regexp@8.4.0", "", {}, "sha512-PuseHIvAnz3bjrM2rGJtSgo1zjgxapTLZ7x2pjhzWwlp4SJQgK3f3iZIQwkpEnBaKz6seKBADpM4B4ySkuYypg=="], + + "sane/anymatch": ["anymatch@2.0.0", "", { "dependencies": { "micromatch": "3.1.10", "normalize-path": "2.1.1" } }, "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw=="], + + "sane/execa": ["execa@1.0.0", "", { "dependencies": { "cross-spawn": "6.0.6", "get-stream": "4.1.0", "is-stream": "1.1.0", "npm-run-path": "2.0.2", "p-finally": "1.0.0", "signal-exit": "3.0.7", "strip-eof": "1.0.0" } }, "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA=="], + + "sane/micromatch": ["micromatch@3.1.10", "", { "dependencies": { "arr-diff": "4.0.0", "array-unique": "0.3.2", "braces": "2.3.2", "define-property": "2.0.2", "extend-shallow": "3.0.2", "extglob": "2.0.4", "fragment-cache": "0.2.1", "kind-of": "6.0.3", "nanomatch": "1.2.13", "object.pick": "1.3.0", "regex-not": "1.0.2", "snapdragon": "0.8.2", "to-regex": "3.0.2" } }, "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg=="], + + "send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + + "send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="], + + "set-value/extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "0.1.1" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], + + "set-value/is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], + + "sharp/semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], + + "slice-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + + "snapdragon/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + + "snapdragon/define-property": ["define-property@0.2.5", "", { "dependencies": { "is-descriptor": "0.1.7" } }, "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA=="], + + "snapdragon/extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "0.1.1" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], + + "snapdragon/source-map": ["source-map@0.5.7", "", {}, "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="], + + "snapdragon-node/define-property": ["define-property@1.0.0", "", { "dependencies": { "is-descriptor": "1.0.3" } }, "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA=="], + + "snapdragon-util/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "1.1.6" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="], + + "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + + "stack-utils/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], + + "static-extend/define-property": ["define-property@0.2.5", "", { "dependencies": { "is-descriptor": "0.1.7" } }, "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA=="], + + "string-length/strip-ansi": ["strip-ansi@4.0.0", "", { "dependencies": { "ansi-regex": "3.0.1" } }, "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow=="], + + "string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="], + + "string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "6.1.0" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + + "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "string-width-cjs/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "string_decoder/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + + "sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], + + "sucrase/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], + + "tinyglobby/fdir": ["fdir@6.4.3", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw=="], + + "to-object-path/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "1.1.6" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="], + + "tsconfig-paths/json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "1.2.8" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="], + + "tsup/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "tsup/esbuild": ["esbuild@0.25.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.2", "@esbuild/android-arm": "0.25.2", "@esbuild/android-arm64": "0.25.2", "@esbuild/android-x64": "0.25.2", "@esbuild/darwin-arm64": "0.25.2", "@esbuild/darwin-x64": "0.25.2", "@esbuild/freebsd-arm64": "0.25.2", "@esbuild/freebsd-x64": "0.25.2", "@esbuild/linux-arm": "0.25.2", "@esbuild/linux-arm64": "0.25.2", "@esbuild/linux-ia32": "0.25.2", "@esbuild/linux-loong64": "0.25.2", "@esbuild/linux-mips64el": "0.25.2", "@esbuild/linux-ppc64": "0.25.2", "@esbuild/linux-riscv64": "0.25.2", "@esbuild/linux-s390x": "0.25.2", "@esbuild/linux-x64": "0.25.2", "@esbuild/netbsd-arm64": "0.25.2", "@esbuild/netbsd-x64": "0.25.2", "@esbuild/openbsd-arm64": "0.25.2", "@esbuild/openbsd-x64": "0.25.2", "@esbuild/sunos-x64": "0.25.2", "@esbuild/win32-arm64": "0.25.2", "@esbuild/win32-ia32": "0.25.2", "@esbuild/win32-x64": "0.25.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ=="], + + "tsutils/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], + + "tsx/esbuild": ["esbuild@0.28.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.2", "@esbuild/android-arm": "0.28.2", "@esbuild/android-arm64": "0.28.2", "@esbuild/android-x64": "0.28.2", "@esbuild/darwin-arm64": "0.28.2", "@esbuild/darwin-x64": "0.28.2", "@esbuild/freebsd-arm64": "0.28.2", "@esbuild/freebsd-x64": "0.28.2", "@esbuild/linux-arm": "0.28.2", "@esbuild/linux-arm64": "0.28.2", "@esbuild/linux-ia32": "0.28.2", "@esbuild/linux-loong64": "0.28.2", "@esbuild/linux-mips64el": "0.28.2", "@esbuild/linux-ppc64": "0.28.2", "@esbuild/linux-riscv64": "0.28.2", "@esbuild/linux-s390x": "0.28.2", "@esbuild/linux-x64": "0.28.2", "@esbuild/netbsd-arm64": "0.28.2", "@esbuild/netbsd-x64": "0.28.2", "@esbuild/openbsd-arm64": "0.28.2", "@esbuild/openbsd-x64": "0.28.2", "@esbuild/openharmony-arm64": "0.28.2", "@esbuild/sunos-x64": "0.28.2", "@esbuild/win32-arm64": "0.28.2", "@esbuild/win32-ia32": "0.28.2", "@esbuild/win32-x64": "0.28.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA=="], + + "type-is/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "union-value/is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], + + "unset-value/has-value": ["has-value@0.3.1", "", { "dependencies": { "get-value": "2.0.6", "has-values": "0.1.4", "isobject": "2.1.0" } }, "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q=="], + + "vite/esbuild": ["esbuild@0.28.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.2", "@esbuild/android-arm": "0.28.2", "@esbuild/android-arm64": "0.28.2", "@esbuild/android-x64": "0.28.2", "@esbuild/darwin-arm64": "0.28.2", "@esbuild/darwin-x64": "0.28.2", "@esbuild/freebsd-arm64": "0.28.2", "@esbuild/freebsd-x64": "0.28.2", "@esbuild/linux-arm": "0.28.2", "@esbuild/linux-arm64": "0.28.2", "@esbuild/linux-ia32": "0.28.2", "@esbuild/linux-loong64": "0.28.2", "@esbuild/linux-mips64el": "0.28.2", "@esbuild/linux-ppc64": "0.28.2", "@esbuild/linux-riscv64": "0.28.2", "@esbuild/linux-s390x": "0.28.2", "@esbuild/linux-x64": "0.28.2", "@esbuild/netbsd-arm64": "0.28.2", "@esbuild/netbsd-x64": "0.28.2", "@esbuild/openbsd-arm64": "0.28.2", "@esbuild/openbsd-x64": "0.28.2", "@esbuild/openharmony-arm64": "0.28.2", "@esbuild/sunos-x64": "0.28.2", "@esbuild/win32-arm64": "0.28.2", "@esbuild/win32-ia32": "0.28.2", "@esbuild/win32-x64": "0.28.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA=="], + + "vite/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "6.5.0", "picomatch": "4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + + "wrap-ansi/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "6.1.0" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + + "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "8.0.0", "is-fullwidth-code-point": "3.0.0", "strip-ansi": "6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "yargs/find-up": ["find-up@3.0.0", "", { "dependencies": { "locate-path": "3.0.0" } }, "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg=="], + + "yargs/string-width": ["string-width@3.1.0", "", { "dependencies": { "emoji-regex": "7.0.3", "is-fullwidth-code-point": "2.0.0", "strip-ansi": "5.2.0" } }, "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w=="], + + "@changesets/parse/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "1.0.3" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="], + + "@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="], + + "@example/next-app-router/eslint-config-next/@next/eslint-plugin-next": ["@next/eslint-plugin-next@15.3.1", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-oEs4dsfM6iyER3jTzMm4kDSbrQJq8wZw5fmT6fg2V3SMo+kgG+cShzLfEV20senZzv8VF+puNLheiGPlBGsv2A=="], + + "@example/next-app-router/eslint-config-next/@typescript-eslint/parser": ["@typescript-eslint/parser@5.62.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", "@typescript-eslint/typescript-estree": "5.62.0", "debug": "4.4.0" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0", "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA=="], + + "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], + + "@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + + "@jest/console/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "@jest/console/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "@jest/core/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "@jest/core/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "@jest/core/micromatch/braces": ["braces@2.3.2", "", { "dependencies": { "arr-flatten": "1.1.0", "array-unique": "0.3.2", "extend-shallow": "2.0.1", "fill-range": "4.0.0", "isobject": "3.0.1", "repeat-element": "1.1.4", "snapdragon": "0.8.2", "snapdragon-node": "2.1.1", "split-string": "3.1.0", "to-regex": "3.0.2" } }, "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w=="], + + "@jest/core/strip-ansi/ansi-regex": ["ansi-regex@4.1.1", "", {}, "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="], + + "@jest/reporters/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "@jest/reporters/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "@jest/transform/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "@jest/transform/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "@jest/transform/micromatch/braces": ["braces@2.3.2", "", { "dependencies": { "arr-flatten": "1.1.0", "array-unique": "0.3.2", "extend-shallow": "2.0.1", "fill-range": "4.0.0", "isobject": "3.0.1", "repeat-element": "1.1.4", "snapdragon": "0.8.2", "snapdragon-node": "2.1.1", "split-string": "3.1.0", "to-regex": "3.0.2" } }, "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w=="], + + "@manypkg/find-root/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], + + "@medialit/api/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "@medialit/docs/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "@medialit/web/eslint/@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.5.1", "", { "dependencies": { "eslint-visitor-keys": "3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w=="], + + "@medialit/web/eslint/@eslint/eslintrc": ["@eslint/eslintrc@2.1.4", "", { "dependencies": { "ajv": "6.12.6", "debug": "4.4.3", "espree": "9.6.1", "globals": "13.24.0", "ignore": "5.3.2", "import-fresh": "3.3.1", "js-yaml": "4.1.1", "minimatch": "3.1.2", "strip-json-comments": "3.1.1" } }, "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ=="], + + "@medialit/web/eslint/@eslint/js": ["@eslint/js@8.57.1", "", {}, "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q=="], + + "@medialit/web/eslint/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "@medialit/web/eslint/doctrine": ["doctrine@3.0.0", "", { "dependencies": { "esutils": "2.0.3" } }, "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w=="], + + "@medialit/web/eslint/eslint-scope": ["eslint-scope@7.2.2", "", { "dependencies": { "esrecurse": "4.3.0", "estraverse": "5.3.0" } }, "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg=="], + + "@medialit/web/eslint/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@medialit/web/eslint/espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "8.14.1", "acorn-jsx": "5.3.2", "eslint-visitor-keys": "3.4.3" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="], + + "@medialit/web/eslint/file-entry-cache": ["file-entry-cache@6.0.1", "", { "dependencies": { "flat-cache": "3.2.0" } }, "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg=="], + + "@medialit/web/eslint/globals": ["globals@13.24.0", "", { "dependencies": { "type-fest": "0.20.2" } }, "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ=="], + + "@medialit/web/tailwindcss/chokidar": ["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" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], + + "@medialit/web/tailwindcss/fast-glob": ["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" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "@medialit/web/tailwindcss/postcss-load-config": ["postcss-load-config@4.0.2", "", { "dependencies": { "lilconfig": "3.1.3", "yaml": "2.7.1" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ=="], + + "@medialit/web/tailwindcss/resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "2.16.1", "path-parse": "1.0.7", "supports-preserve-symlinks-flag": "1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], + + "@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "@modelcontextprotocol/sdk/express/accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "3.0.2", "negotiator": "1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], + + "@modelcontextprotocol/sdk/express/body-parser": ["body-parser@2.3.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^2.0.0", "debug": "^4.4.3", "http-errors": "^2.0.1", "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", "qs": "^6.15.2", "raw-body": "^3.0.2", "type-is": "^2.1.0" } }, "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw=="], + + "@modelcontextprotocol/sdk/express/content-disposition": ["content-disposition@1.1.0", "", {}, "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g=="], + + "@modelcontextprotocol/sdk/express/cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], + + "@modelcontextprotocol/sdk/express/finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "4.4.3", "encodeurl": "2.0.0", "escape-html": "1.0.3", "on-finished": "2.4.1", "parseurl": "1.3.3", "statuses": "2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], + + "@modelcontextprotocol/sdk/express/fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], + + "@modelcontextprotocol/sdk/express/merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + + "@modelcontextprotocol/sdk/express/send": ["send@1.2.1", "", { "dependencies": { "debug": "4.4.3", "encodeurl": "2.0.0", "escape-html": "1.0.3", "etag": "1.8.1", "fresh": "2.0.0", "http-errors": "2.0.1", "mime-types": "3.0.2", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "1.2.1", "statuses": "2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], + + "@modelcontextprotocol/sdk/express/serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "2.0.0", "escape-html": "1.0.3", "parseurl": "1.3.3", "send": "1.2.1" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], + + "@modelcontextprotocol/sdk/express/type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "2.0.0", "media-typer": "1.1.0", "mime-types": "3.0.2" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="], + + "@radix-ui/react-accordion/@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-accordion/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-arrow/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-collapsible/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-dismissable-layer/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-focus-scope/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-menu/@radix-ui/react-dismissable-layer/@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw=="], + + "@radix-ui/react-menu/@radix-ui/react-popper/@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.2", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg=="], + + "@radix-ui/react-menu/@radix-ui/react-popper/@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.0", "", { "dependencies": { "@radix-ui/rect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ=="], + + "@radix-ui/react-menu/@radix-ui/react-popper/@radix-ui/rect": ["@radix-ui/rect@1.1.0", "", {}, "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg=="], + + "@radix-ui/react-menu/@radix-ui/react-roving-focus/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw=="], + + "@radix-ui/react-navigation-menu/@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-navigation-menu/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-popper/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-portal/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="], + + "@radix-ui/react-progress/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w=="], + + "@radix-ui/react-roving-focus/@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-roving-focus/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-scroll-area/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-tabs/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-toast/@radix-ui/react-dismissable-layer/@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw=="], + + "@types/babel__core/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.25.9", "", {}, "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="], + + "@types/babel__core/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="], + + "@types/babel__generator/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.25.9", "", {}, "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="], + + "@types/babel__generator/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="], + + "@types/babel__template/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.25.9", "", {}, "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="], + + "@types/babel__template/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="], + + "@types/babel__traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.25.9", "", {}, "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="], + + "@types/babel__traverse/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="], + + "@types/pg/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "@typescript-eslint/typescript-estree/fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "4.0.3" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.12", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ=="], + + "@typescript-eslint/utils/@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "babel-jest/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "babel-jest/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "babel-plugin-istanbul/find-up/locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "3.0.0", "path-exists": "3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="], + + "body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + + "body-parser/http-errors/statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + + "chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "class-utils/define-property/is-descriptor": ["is-descriptor@0.1.7", "", { "dependencies": { "is-accessor-descriptor": "1.0.1", "is-data-descriptor": "1.0.1" } }, "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg=="], + + "cliui/string-width/emoji-regex": ["emoji-regex@7.0.3", "", {}, "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="], + + "cliui/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@2.0.0", "", {}, "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w=="], + + "cliui/strip-ansi/ansi-regex": ["ansi-regex@4.1.1", "", {}, "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="], + + "cliui/wrap-ansi/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "escodegen/optionator/levn": ["levn@0.3.0", "", { "dependencies": { "prelude-ls": "1.1.2", "type-check": "0.3.2" } }, "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA=="], + + "escodegen/optionator/prelude-ls": ["prelude-ls@1.1.2", "", {}, "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w=="], + + "escodegen/optionator/type-check": ["type-check@0.3.2", "", { "dependencies": { "prelude-ls": "1.1.2" } }, "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg=="], + + "eslint-config-next/@typescript-eslint/parser/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@5.62.0", "", { "dependencies": { "@typescript-eslint/types": "5.62.0", "@typescript-eslint/visitor-keys": "5.62.0" } }, "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w=="], + + "eslint-config-next/@typescript-eslint/parser/@typescript-eslint/types": ["@typescript-eslint/types@5.62.0", "", {}, "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ=="], + + "eslint-config-next/@typescript-eslint/parser/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@5.62.0", "", { "dependencies": { "@typescript-eslint/types": "5.62.0", "@typescript-eslint/visitor-keys": "5.62.0", "debug": "4.4.0", "globby": "11.1.0", "is-glob": "4.0.3", "semver": "7.7.1", "tsutils": "3.21.0" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA=="], + + "eslint-config-next/@typescript-eslint/parser/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "expand-brackets/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + + "expand-brackets/define-property/is-descriptor": ["is-descriptor@0.1.7", "", { "dependencies": { "is-accessor-descriptor": "1.0.1", "is-data-descriptor": "1.0.1" } }, "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg=="], + + "expand-brackets/extend-shallow/is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], + + "expect/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + + "extglob/extend-shallow/is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], + + "finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + + "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "fumadocs-mdx/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], + + "fumadocs-mdx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q=="], + + "fumadocs-mdx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.4", "", { "os": "android", "cpu": "arm" }, "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ=="], + + "fumadocs-mdx/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.4", "", { "os": "android", "cpu": "arm64" }, "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw=="], + + "fumadocs-mdx/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.4", "", { "os": "android", "cpu": "x64" }, "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw=="], + + "fumadocs-mdx/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ=="], + + "fumadocs-mdx/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw=="], + + "fumadocs-mdx/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw=="], + + "fumadocs-mdx/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.4", "", { "os": "linux", "cpu": "arm" }, "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.4", "", { "os": "linux", "cpu": "ia32" }, "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.4", "", { "os": "linux", "cpu": "none" }, "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.4", "", { "os": "linux", "cpu": "none" }, "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.4", "", { "os": "linux", "cpu": "none" }, "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.4", "", { "os": "linux", "cpu": "x64" }, "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA=="], + + "fumadocs-mdx/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.4", "", { "os": "none", "cpu": "arm64" }, "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q=="], + + "fumadocs-mdx/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.4", "", { "os": "none", "cpu": "x64" }, "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg=="], + + "fumadocs-mdx/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.4", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow=="], + + "fumadocs-mdx/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.4", "", { "os": "openbsd", "cpu": "x64" }, "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ=="], + + "fumadocs-mdx/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.4", "", { "os": "none", "cpu": "arm64" }, "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg=="], + + "fumadocs-mdx/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.4", "", { "os": "sunos", "cpu": "x64" }, "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g=="], + + "fumadocs-mdx/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg=="], + + "fumadocs-mdx/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw=="], + + "fumadocs-mdx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.4", "", { "os": "win32", "cpu": "x64" }, "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg=="], + + "fumadocs-openapi/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "globby/fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "4.0.3" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "has-values/is-number/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "1.1.6" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="], + + "istanbul-lib-instrument/@babel/template/@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "7.25.9", "js-tokens": "4.0.0", "picocolors": "1.1.1" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="], + + "istanbul-lib-instrument/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "7.25.9", "js-tokens": "4.0.0", "picocolors": "1.1.1" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="], + + "istanbul-lib-instrument/@babel/traverse/globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="], + + "istanbul-lib-instrument/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.25.9", "", {}, "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="], + + "istanbul-lib-instrument/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="], + + "jest-changed-files/execa/cross-spawn": ["cross-spawn@6.0.6", "", { "dependencies": { "nice-try": "1.0.5", "path-key": "2.0.1", "semver": "5.7.2", "shebang-command": "1.2.0", "which": "1.3.1" } }, "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw=="], + + "jest-changed-files/execa/get-stream": ["get-stream@4.1.0", "", { "dependencies": { "pump": "3.0.2" } }, "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w=="], + + "jest-changed-files/execa/is-stream": ["is-stream@1.1.0", "", {}, "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ=="], + + "jest-changed-files/execa/npm-run-path": ["npm-run-path@2.0.2", "", { "dependencies": { "path-key": "2.0.1" } }, "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw=="], + + "jest-changed-files/execa/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "jest-cli/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "jest-cli/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "jest-config/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "jest-config/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "jest-config/micromatch/braces": ["braces@2.3.2", "", { "dependencies": { "arr-flatten": "1.1.0", "array-unique": "0.3.2", "extend-shallow": "2.0.1", "fill-range": "4.0.0", "isobject": "3.0.1", "repeat-element": "1.1.4", "snapdragon": "0.8.2", "snapdragon-node": "2.1.1", "split-string": "3.1.0", "to-regex": "3.0.2" } }, "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w=="], + + "jest-diff/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "jest-diff/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "jest-each/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "jest-each/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "jest-haste-map/anymatch/normalize-path": ["normalize-path@2.1.1", "", { "dependencies": { "remove-trailing-separator": "1.1.0" } }, "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w=="], + + "jest-haste-map/micromatch/braces": ["braces@2.3.2", "", { "dependencies": { "arr-flatten": "1.1.0", "array-unique": "0.3.2", "extend-shallow": "2.0.1", "fill-range": "4.0.0", "isobject": "3.0.1", "repeat-element": "1.1.4", "snapdragon": "0.8.2", "snapdragon-node": "2.1.1", "split-string": "3.1.0", "to-regex": "3.0.2" } }, "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w=="], + + "jest-jasmine2/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "7.25.9", "js-tokens": "4.0.0", "picocolors": "1.1.1" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="], + + "jest-jasmine2/@babel/traverse/@babel/generator": ["@babel/generator@7.27.0", "", { "dependencies": { "@babel/parser": "7.27.0", "@babel/types": "7.27.0", "@jridgewell/gen-mapping": "0.3.8", "@jridgewell/trace-mapping": "0.3.25", "jsesc": "3.1.0" } }, "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw=="], + + "jest-jasmine2/@babel/traverse/@babel/parser": ["@babel/parser@7.27.0", "", { "dependencies": { "@babel/types": "7.27.0" }, "bin": "./bin/babel-parser.js" }, "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg=="], + + "jest-jasmine2/@babel/traverse/@babel/template": ["@babel/template@7.27.0", "", { "dependencies": { "@babel/code-frame": "7.26.2", "@babel/parser": "7.27.0", "@babel/types": "7.27.0" } }, "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA=="], + + "jest-jasmine2/@babel/traverse/@babel/types": ["@babel/types@7.27.0", "", { "dependencies": { "@babel/helper-string-parser": "7.25.9", "@babel/helper-validator-identifier": "7.25.9" } }, "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg=="], + + "jest-jasmine2/@babel/traverse/globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="], + + "jest-jasmine2/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "jest-jasmine2/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "jest-matcher-utils/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "jest-matcher-utils/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "jest-message-util/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="], + + "jest-message-util/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "jest-message-util/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "jest-message-util/micromatch/braces": ["braces@2.3.2", "", { "dependencies": { "arr-flatten": "1.1.0", "array-unique": "0.3.2", "extend-shallow": "2.0.1", "fill-range": "4.0.0", "isobject": "3.0.1", "repeat-element": "1.1.4", "snapdragon": "0.8.2", "snapdragon-node": "2.1.1", "split-string": "3.1.0", "to-regex": "3.0.2" } }, "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w=="], + + "jest-resolve/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "jest-resolve/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "jest-runner/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "jest-runner/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "jest-runtime/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "jest-runtime/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "jest-snapshot/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.25.9", "", {}, "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="], + + "jest-snapshot/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="], + + "jest-snapshot/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "jest-snapshot/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "jest-util/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "jest-util/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "jest-validate/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "jest-validate/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "jest-watcher/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "1.9.3" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], + + "jest-watcher/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "log-update/slice-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + + "log-update/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.0.0", "", { "dependencies": { "get-east-asian-width": "1.3.0" } }, "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA=="], + + "log-update/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], + + "mongodb-connection-string-url/whatwg-url/tr46": ["tr46@5.1.0", "", { "dependencies": { "punycode": "2.3.1" } }, "sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw=="], + + "mongodb-connection-string-url/whatwg-url/webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], + + "object-copy/define-property/is-descriptor": ["is-descriptor@0.1.7", "", { "dependencies": { "is-accessor-descriptor": "1.0.1", "is-data-descriptor": "1.0.1" } }, "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg=="], + + "pkg-dir/find-up/locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "3.0.0", "path-exists": "3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="], + + "pretty-format/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "qs/side-channel/side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], + + "raw-body/http-errors/statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + + "read-pkg-up/find-up/locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "3.0.0", "path-exists": "3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="], + + "read-pkg/path-type/pify": ["pify@3.0.0", "", {}, "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="], + + "read-yaml-file/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "1.0.3" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + + "request/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "sane/anymatch/normalize-path": ["normalize-path@2.1.1", "", { "dependencies": { "remove-trailing-separator": "1.1.0" } }, "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w=="], + + "sane/execa/cross-spawn": ["cross-spawn@6.0.6", "", { "dependencies": { "nice-try": "1.0.5", "path-key": "2.0.1", "semver": "5.7.2", "shebang-command": "1.2.0", "which": "1.3.1" } }, "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw=="], + + "sane/execa/get-stream": ["get-stream@4.1.0", "", { "dependencies": { "pump": "3.0.2" } }, "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w=="], + + "sane/execa/is-stream": ["is-stream@1.1.0", "", {}, "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ=="], + + "sane/execa/npm-run-path": ["npm-run-path@2.0.2", "", { "dependencies": { "path-key": "2.0.1" } }, "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw=="], + + "sane/execa/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "sane/micromatch/braces": ["braces@2.3.2", "", { "dependencies": { "arr-flatten": "1.1.0", "array-unique": "0.3.2", "extend-shallow": "2.0.1", "fill-range": "4.0.0", "isobject": "3.0.1", "repeat-element": "1.1.4", "snapdragon": "0.8.2", "snapdragon-node": "2.1.1", "split-string": "3.1.0", "to-regex": "3.0.2" } }, "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w=="], + + "send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + + "snapdragon/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + + "snapdragon/define-property/is-descriptor": ["is-descriptor@0.1.7", "", { "dependencies": { "is-accessor-descriptor": "1.0.1", "is-data-descriptor": "1.0.1" } }, "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg=="], + + "snapdragon/extend-shallow/is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], + + "static-extend/define-property/is-descriptor": ["is-descriptor@0.1.7", "", { "dependencies": { "is-accessor-descriptor": "1.0.1", "is-data-descriptor": "1.0.1" } }, "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg=="], + + "string-length/strip-ansi/ansi-regex": ["ansi-regex@3.0.1", "", {}, "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw=="], + + "string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], + + "sucrase/glob/minimatch": ["minimatch@9.0.7", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-MOwgjc8tfrpn5QQEvjijjmDVtMw2oL88ugTevzxQnzRLm6l3fVEF2gzU0kYeYYKD8C66+IdGX6peJ4MyUlUnPg=="], + + "tsup/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag=="], + + "tsup/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.2", "", { "os": "android", "cpu": "arm" }, "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA=="], + + "tsup/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.2", "", { "os": "android", "cpu": "arm64" }, "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w=="], + + "tsup/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.2", "", { "os": "android", "cpu": "x64" }, "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg=="], + + "tsup/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA=="], + + "tsup/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA=="], + + "tsup/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w=="], + + "tsup/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ=="], + + "tsup/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.2", "", { "os": "linux", "cpu": "arm" }, "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g=="], + + "tsup/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g=="], + + "tsup/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ=="], + + "tsup/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.2", "", { "os": "linux", "cpu": "none" }, "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w=="], + + "tsup/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.2", "", { "os": "linux", "cpu": "none" }, "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q=="], + + "tsup/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g=="], + + "tsup/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.2", "", { "os": "linux", "cpu": "none" }, "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw=="], + + "tsup/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q=="], + + "tsup/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.2", "", { "os": "linux", "cpu": "x64" }, "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg=="], + + "tsup/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.2", "", { "os": "none", "cpu": "arm64" }, "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw=="], + + "tsup/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.2", "", { "os": "none", "cpu": "x64" }, "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg=="], + + "tsup/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg=="], + + "tsup/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw=="], + + "tsup/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA=="], + + "tsup/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q=="], + + "tsup/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg=="], + + "tsup/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.2", "", { "os": "win32", "cpu": "x64" }, "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA=="], + + "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ=="], + + "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.28.2", "", { "os": "android", "cpu": "arm" }, "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg=="], + + "tsx/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.2", "", { "os": "android", "cpu": "arm64" }, "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A=="], + + "tsx/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.28.2", "", { "os": "android", "cpu": "x64" }, "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q=="], + + "tsx/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw=="], + + "tsx/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw=="], + + "tsx/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw=="], + + "tsx/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg=="], + + "tsx/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.2", "", { "os": "linux", "cpu": "arm" }, "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w=="], + + "tsx/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug=="], + + "tsx/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ=="], + + "tsx/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.2", "", { "os": "linux", "cpu": "none" }, "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ=="], + + "tsx/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.2", "", { "os": "linux", "cpu": "none" }, "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA=="], + + "tsx/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ=="], + + "tsx/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.2", "", { "os": "linux", "cpu": "none" }, "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA=="], + + "tsx/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg=="], + + "tsx/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.2", "", { "os": "linux", "cpu": "x64" }, "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ=="], + + "tsx/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.2", "", { "os": "none", "cpu": "arm64" }, "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw=="], + + "tsx/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.2", "", { "os": "none", "cpu": "x64" }, "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw=="], + + "tsx/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ=="], + + "tsx/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw=="], + + "tsx/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.2", "", { "os": "none", "cpu": "arm64" }, "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q=="], + + "tsx/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g=="], + + "tsx/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ=="], + + "tsx/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA=="], + + "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g=="], + + "type-is/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "unset-value/has-value/has-values": ["has-values@0.1.4", "", {}, "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ=="], + + "unset-value/has-value/isobject": ["isobject@2.1.0", "", { "dependencies": { "isarray": "1.0.0" } }, "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA=="], + + "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ=="], + + "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.28.2", "", { "os": "android", "cpu": "arm" }, "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg=="], + + "vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.2", "", { "os": "android", "cpu": "arm64" }, "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A=="], + + "vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.28.2", "", { "os": "android", "cpu": "x64" }, "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q=="], + + "vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw=="], + + "vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw=="], + + "vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw=="], + + "vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg=="], + + "vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.2", "", { "os": "linux", "cpu": "arm" }, "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w=="], + + "vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug=="], + + "vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ=="], + + "vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.2", "", { "os": "linux", "cpu": "none" }, "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ=="], + + "vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.2", "", { "os": "linux", "cpu": "none" }, "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA=="], + + "vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ=="], + + "vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.2", "", { "os": "linux", "cpu": "none" }, "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA=="], + + "vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg=="], + + "vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.2", "", { "os": "linux", "cpu": "x64" }, "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ=="], + + "vite/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.2", "", { "os": "none", "cpu": "arm64" }, "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw=="], + + "vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.2", "", { "os": "none", "cpu": "x64" }, "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw=="], + + "vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ=="], + + "vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw=="], + + "vite/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.2", "", { "os": "none", "cpu": "arm64" }, "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q=="], + + "vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g=="], + + "vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ=="], + + "vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA=="], + + "vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g=="], + + "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "wrap-ansi-cjs/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], + + "yargs/find-up/locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "3.0.0", "path-exists": "3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="], + + "yargs/string-width/emoji-regex": ["emoji-regex@7.0.3", "", {}, "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="], + + "yargs/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@2.0.0", "", {}, "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w=="], + + "yargs/string-width/strip-ansi": ["strip-ansi@5.2.0", "", { "dependencies": { "ansi-regex": "4.1.1" } }, "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA=="], + + "@example/next-app-router/eslint-config-next/@typescript-eslint/parser/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@5.62.0", "", { "dependencies": { "@typescript-eslint/types": "5.62.0", "@typescript-eslint/visitor-keys": "5.62.0" } }, "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w=="], + + "@example/next-app-router/eslint-config-next/@typescript-eslint/parser/@typescript-eslint/types": ["@typescript-eslint/types@5.62.0", "", {}, "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ=="], + + "@example/next-app-router/eslint-config-next/@typescript-eslint/parser/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@5.62.0", "", { "dependencies": { "@typescript-eslint/types": "5.62.0", "@typescript-eslint/visitor-keys": "5.62.0", "debug": "4.4.0", "globby": "11.1.0", "is-glob": "4.0.3", "semver": "7.7.1", "tsutils": "3.21.0" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA=="], + + "@example/next-app-router/eslint-config-next/@typescript-eslint/parser/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "@jest/console/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "@jest/core/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "@jest/core/micromatch/braces/extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "0.1.1" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], + + "@jest/core/micromatch/braces/fill-range": ["fill-range@4.0.0", "", { "dependencies": { "extend-shallow": "2.0.1", "is-number": "3.0.0", "repeat-string": "1.6.1", "to-regex-range": "2.1.1" } }, "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ=="], + + "@jest/reporters/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "@jest/transform/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "@jest/transform/micromatch/braces/extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "0.1.1" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], + + "@jest/transform/micromatch/braces/fill-range": ["fill-range@4.0.0", "", { "dependencies": { "extend-shallow": "2.0.1", "is-number": "3.0.0", "repeat-string": "1.6.1", "to-regex-range": "2.1.1" } }, "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ=="], + + "@manypkg/find-root/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "2.3.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], + + "@medialit/web/eslint/@eslint/eslintrc/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "@medialit/web/eslint/espree/acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="], + + "@medialit/web/eslint/file-entry-cache/flat-cache": ["flat-cache@3.2.0", "", { "dependencies": { "flatted": "3.3.3", "keyv": "4.5.4", "rimraf": "3.0.2" } }, "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw=="], + + "@medialit/web/tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "4.0.3" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "@medialit/web/tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "2.3.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], + + "@medialit/web/tailwindcss/fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "4.0.3" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "@modelcontextprotocol/sdk/express/body-parser/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="], + + "@modelcontextprotocol/sdk/express/body-parser/http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.2", "toidentifier": "1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + + "@modelcontextprotocol/sdk/express/body-parser/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": "2.1.2" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + + "@modelcontextprotocol/sdk/express/send/http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.2", "toidentifier": "1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + + "@modelcontextprotocol/sdk/express/send/statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + + "@modelcontextprotocol/sdk/express/type-is/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="], + + "@modelcontextprotocol/sdk/express/type-is/media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + + "babel-jest/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "babel-plugin-istanbul/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "2.3.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="], + + "babel-plugin-istanbul/find-up/locate-path/path-exists": ["path-exists@3.0.0", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="], + + "cliui/wrap-ansi/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "eslint-config-next/@typescript-eslint/parser/@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@5.62.0", "", { "dependencies": { "@typescript-eslint/types": "5.62.0", "eslint-visitor-keys": "3.4.3" } }, "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw=="], + + "eslint-config-next/@typescript-eslint/parser/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@5.62.0", "", { "dependencies": { "@typescript-eslint/types": "5.62.0", "eslint-visitor-keys": "3.4.3" } }, "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw=="], + + "expect/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "istanbul-lib-instrument/@babel/template/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="], + + "istanbul-lib-instrument/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="], + + "jest-changed-files/execa/cross-spawn/path-key": ["path-key@2.0.1", "", {}, "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="], + + "jest-changed-files/execa/cross-spawn/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], + + "jest-changed-files/execa/cross-spawn/shebang-command": ["shebang-command@1.2.0", "", { "dependencies": { "shebang-regex": "1.0.0" } }, "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg=="], + + "jest-changed-files/execa/cross-spawn/which": ["which@1.3.1", "", { "dependencies": { "isexe": "2.0.0" }, "bin": { "which": "./bin/which" } }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="], + + "jest-changed-files/execa/npm-run-path/path-key": ["path-key@2.0.1", "", {}, "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="], + + "jest-cli/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "jest-config/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "jest-config/micromatch/braces/extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "0.1.1" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], + + "jest-config/micromatch/braces/fill-range": ["fill-range@4.0.0", "", { "dependencies": { "extend-shallow": "2.0.1", "is-number": "3.0.0", "repeat-string": "1.6.1", "to-regex-range": "2.1.1" } }, "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ=="], + + "jest-diff/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "jest-each/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "jest-haste-map/micromatch/braces/extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "0.1.1" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], + + "jest-haste-map/micromatch/braces/fill-range": ["fill-range@4.0.0", "", { "dependencies": { "extend-shallow": "2.0.1", "is-number": "3.0.0", "repeat-string": "1.6.1", "to-regex-range": "2.1.1" } }, "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ=="], + + "jest-jasmine2/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="], + + "jest-jasmine2/@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.25.9", "", {}, "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="], + + "jest-jasmine2/@babel/traverse/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="], + + "jest-jasmine2/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "jest-matcher-utils/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "jest-message-util/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "jest-message-util/micromatch/braces/extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "0.1.1" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], + + "jest-message-util/micromatch/braces/fill-range": ["fill-range@4.0.0", "", { "dependencies": { "extend-shallow": "2.0.1", "is-number": "3.0.0", "repeat-string": "1.6.1", "to-regex-range": "2.1.1" } }, "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ=="], + + "jest-resolve/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "jest-runner/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "jest-runtime/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "jest-snapshot/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "jest-util/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "jest-validate/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "jest-watcher/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + + "pkg-dir/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "2.3.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="], + + "pkg-dir/find-up/locate-path/path-exists": ["path-exists@3.0.0", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="], + + "pretty-format/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "read-pkg-up/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "2.3.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="], + + "read-pkg-up/find-up/locate-path/path-exists": ["path-exists@3.0.0", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="], + + "sane/execa/cross-spawn/path-key": ["path-key@2.0.1", "", {}, "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="], + + "sane/execa/cross-spawn/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], + + "sane/execa/cross-spawn/shebang-command": ["shebang-command@1.2.0", "", { "dependencies": { "shebang-regex": "1.0.0" } }, "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg=="], + + "sane/execa/cross-spawn/which": ["which@1.3.1", "", { "dependencies": { "isexe": "2.0.0" }, "bin": { "which": "./bin/which" } }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="], + + "sane/execa/npm-run-path/path-key": ["path-key@2.0.1", "", {}, "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="], + + "sane/micromatch/braces/extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "0.1.1" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], + + "sane/micromatch/braces/fill-range": ["fill-range@4.0.0", "", { "dependencies": { "extend-shallow": "2.0.1", "is-number": "3.0.0", "repeat-string": "1.6.1", "to-regex-range": "2.1.1" } }, "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ=="], + + "sucrase/glob/minimatch/brace-expansion": ["brace-expansion@5.0.12", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ=="], + + "unset-value/has-value/isobject/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], + + "yargs/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "2.3.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="], + + "yargs/find-up/locate-path/path-exists": ["path-exists@3.0.0", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="], + + "yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@4.1.1", "", {}, "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="], + + "@example/next-app-router/eslint-config-next/@typescript-eslint/parser/@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@5.62.0", "", { "dependencies": { "@typescript-eslint/types": "5.62.0", "eslint-visitor-keys": "3.4.3" } }, "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw=="], + + "@example/next-app-router/eslint-config-next/@typescript-eslint/parser/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@5.62.0", "", { "dependencies": { "@typescript-eslint/types": "5.62.0", "eslint-visitor-keys": "3.4.3" } }, "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw=="], + + "@jest/console/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "@jest/core/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "@jest/core/micromatch/braces/extend-shallow/is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], + + "@jest/core/micromatch/braces/fill-range/is-number": ["is-number@3.0.0", "", { "dependencies": { "kind-of": "3.2.2" } }, "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg=="], + + "@jest/core/micromatch/braces/fill-range/to-regex-range": ["to-regex-range@2.1.1", "", { "dependencies": { "is-number": "3.0.0", "repeat-string": "1.6.1" } }, "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg=="], + + "@jest/reporters/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "@jest/transform/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "@jest/transform/micromatch/braces/extend-shallow/is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], + + "@jest/transform/micromatch/braces/fill-range/is-number": ["is-number@3.0.0", "", { "dependencies": { "kind-of": "3.2.2" } }, "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg=="], + + "@jest/transform/micromatch/braces/fill-range/to-regex-range": ["to-regex-range@2.1.1", "", { "dependencies": { "is-number": "3.0.0", "repeat-string": "1.6.1" } }, "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg=="], + + "@medialit/web/eslint/file-entry-cache/flat-cache/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "7.2.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], + + "@medialit/web/tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + + "@modelcontextprotocol/sdk/express/body-parser/http-errors/statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + + "babel-jest/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "cliui/wrap-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "eslint-config-next/@typescript-eslint/parser/@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "eslint-config-next/@typescript-eslint/parser/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "jest-changed-files/execa/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@1.0.0", "", {}, "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ=="], + + "jest-cli/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "jest-config/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "jest-config/micromatch/braces/extend-shallow/is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], + + "jest-config/micromatch/braces/fill-range/is-number": ["is-number@3.0.0", "", { "dependencies": { "kind-of": "3.2.2" } }, "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg=="], + + "jest-config/micromatch/braces/fill-range/to-regex-range": ["to-regex-range@2.1.1", "", { "dependencies": { "is-number": "3.0.0", "repeat-string": "1.6.1" } }, "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg=="], + + "jest-diff/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "jest-each/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "jest-haste-map/micromatch/braces/extend-shallow/is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], + + "jest-haste-map/micromatch/braces/fill-range/is-number": ["is-number@3.0.0", "", { "dependencies": { "kind-of": "3.2.2" } }, "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg=="], + + "jest-haste-map/micromatch/braces/fill-range/to-regex-range": ["to-regex-range@2.1.1", "", { "dependencies": { "is-number": "3.0.0", "repeat-string": "1.6.1" } }, "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg=="], + + "jest-jasmine2/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "jest-matcher-utils/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "jest-message-util/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "jest-message-util/micromatch/braces/extend-shallow/is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], + + "jest-message-util/micromatch/braces/fill-range/is-number": ["is-number@3.0.0", "", { "dependencies": { "kind-of": "3.2.2" } }, "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg=="], + + "jest-message-util/micromatch/braces/fill-range/to-regex-range": ["to-regex-range@2.1.1", "", { "dependencies": { "is-number": "3.0.0", "repeat-string": "1.6.1" } }, "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg=="], + + "jest-resolve/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "jest-runner/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "jest-runtime/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "jest-snapshot/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "jest-util/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "jest-validate/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "jest-watcher/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + + "sane/execa/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@1.0.0", "", {}, "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ=="], + + "sane/micromatch/braces/extend-shallow/is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], + + "sane/micromatch/braces/fill-range/is-number": ["is-number@3.0.0", "", { "dependencies": { "kind-of": "3.2.2" } }, "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg=="], + + "sane/micromatch/braces/fill-range/to-regex-range": ["to-regex-range@2.1.1", "", { "dependencies": { "is-number": "3.0.0", "repeat-string": "1.6.1" } }, "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg=="], + + "sucrase/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + + "@example/next-app-router/eslint-config-next/@typescript-eslint/parser/@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@example/next-app-router/eslint-config-next/@typescript-eslint/parser/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@jest/core/micromatch/braces/fill-range/is-number/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "1.1.6" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="], + + "@jest/transform/micromatch/braces/fill-range/is-number/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "1.1.6" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="], + + "jest-config/micromatch/braces/fill-range/is-number/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "1.1.6" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="], + + "jest-haste-map/micromatch/braces/fill-range/is-number/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "1.1.6" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="], + + "jest-message-util/micromatch/braces/fill-range/is-number/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "1.1.6" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="], + + "sane/micromatch/braces/fill-range/is-number/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "1.1.6" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="], + } +} diff --git a/docker-compose.local.yml b/docker-compose.local.yml new file mode 100644 index 00000000..ca52bfda --- /dev/null +++ b/docker-compose.local.yml @@ -0,0 +1,96 @@ +# Local development dependencies only. The API and web app run on the host: +# +# docker compose -f docker-compose.local.yml up +# bun --filter @medialit/api dev +# bun --filter @medialit/web dev +# +# Copy apps/api/.env.example to apps/api/.env before starting the API. +# Do not run this file together with docker-compose.yml. Both publish +# Postgres on 5433. +# +# Host ports: +# Postgres: 127.0.0.1:5433 +# MinIO S3 API: 127.0.0.1:9000 +# MinIO console: 127.0.0.1:9001 +# Mailpit SMTP: 127.0.0.1:1025 +# Mailpit web UI: 127.0.0.1:8025 +# +# MinIO signs in as medialit / medialit-secret and creates the private +# bucket `medialit` and the public bucket `medialit-public`. Mailpit accepts +# SMTP without authentication. These values are local-only. +name: medialit-local + +x-minio-env: &minio-env + MINIO_ROOT_USER: medialit + MINIO_ROOT_PASSWORD: medialit-secret + +services: + postgres: + image: postgres:17 + restart: unless-stopped + environment: + POSTGRES_USER: medialit + POSTGRES_PASSWORD: medialit + POSTGRES_DB: medialit + ports: + - "127.0.0.1:${POSTGRES_PORT:-5433}:5432" + volumes: + - medialit-local-postgres-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] + interval: 5s + timeout: 5s + retries: 12 + + # MinIO no longer publishes images (quay.io/minio and Docker Hub's + # minio/minio are gone). Chainguard builds it from source; its image also + # includes a shell and mc, which the health check and minio-init use. + minio: + image: cgr.dev/chainguard/minio:latest + restart: unless-stopped + command: server /data --console-address ":9001" + environment: + <<: *minio-env + ports: + - "127.0.0.1:${MINIO_API_PORT:-9000}:9000" + - "127.0.0.1:${MINIO_CONSOLE_PORT:-9001}:9001" + volumes: + - medialit-local-minio-data:/data + healthcheck: + test: ["CMD", "mc", "ready", "local"] + interval: 5s + timeout: 5s + retries: 12 + + minio-init: + image: cgr.dev/chainguard/minio:latest + restart: "no" + depends_on: + minio: + condition: service_healthy + environment: + <<: *minio-env + entrypoint: /bin/sh + command: + - -ec + - | + mc alias set local http://minio:9000 "$$MINIO_ROOT_USER" "$$MINIO_ROOT_PASSWORD" + mc mb --ignore-existing local/medialit + mc mb --ignore-existing local/medialit-public + mc anonymous set download local/medialit-public + + mailpit: + image: axllent/mailpit:latest + restart: unless-stopped + ports: + - "127.0.0.1:${MAILPIT_SMTP_PORT:-1025}:1025" + - "127.0.0.1:${MAILPIT_HTTP_PORT:-8025}:8025" + healthcheck: + test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1:8025/"] + interval: 5s + timeout: 5s + retries: 12 + +volumes: + medialit-local-postgres-data: + medialit-local-minio-data: diff --git a/docker-compose.yml b/docker-compose.yml index 222a2821..ac3232d1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,28 +1,65 @@ version: "3" services: + postgres: + image: postgres:17 + restart: on-failure + environment: + POSTGRES_USER: medialit + POSTGRES_PASSWORD: medialit + POSTGRES_DB: medialit + ports: + - "5433:5432" + volumes: + - medialit-postgres-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U medialit -d medialit"] + interval: 5s + timeout: 5s + retries: 12 + + init: + image: codelit/medialit + restart: "no" + environment: + - DATABASE_URL=postgresql://medialit:medialit@postgres:5432/medialit + depends_on: + postgres: + condition: service_healthy + command: ["bun", "apps/api/src/db/migrate.ts"] + medialit: image: codelit/medialit restart: on-failure environment: - EMAIL=${EMAIL?'Email is required to set up a user'} - - DB_CONNECTION_STRING=${DB_CONNECTION_STRING?'Database connection string is required'} + - DATABASE_URL=postgresql://medialit:medialit@postgres:5432/medialit + - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET?'BETTER_AUTH_SECRET is required'} - CLOUD_ENDPOINT=${CLOUD_ENDPOINT?'Cloud endpoint is required'} - CLOUD_REGION=${CLOUD_REGION?'Cloud region is required'} - CLOUD_KEY=${CLOUD_KEY?'Cloud key is required'} - CLOUD_SECRET=${CLOUD_SECRET?'Cloud secret is required'} - CLOUD_BUCKET_NAME=${CLOUD_BUCKET_NAME?'Cloud bucket name is required'} + - CLOUD_PUBLIC_BUCKET_NAME=${CLOUD_PUBLIC_BUCKET_NAME?'Cloud public bucket name is required'} + - CLOUD_ENDPOINT_PUBLIC=${CLOUD_ENDPOINT_PUBLIC} + - DISABLE_TAGGING=${DISABLE_TAGGING} - PATH_PREFIX=${PATH_PREFIX?'Cloud prefix is required'} - TEMP_FILE_DIR_FOR_UPLOADS=${TEMP_FILE_DIR_FOR_UPLOADS?'A temporary directory for uploads transformations is required'} - PORT=8000 + # OAuth and sign-in redirects use these, so set them to the public URLs. + - PUBLIC_API_URL=${PUBLIC_API_URL?'Public API URL is required, for example https://medialit.example.com'} + - WEB_ORIGIN=${WEB_ORIGIN} - ENABLE_TRUST_PROXY=${ENABLE_TRUST_PROXY} - - ACCESS_PRIVATE_BUCKET_VIA_CLOUDFRONT=${ACCESS_PRIVATE_BUCKET_VIA_CLOUDFRONT} - - CDN_ENDPOINT=${CDN_ENDPOINT} - - CLOUDFRONT_KEY_PAIR_ID=${CLOUDFRONT_KEY_PAIR_ID} + - ACCESS_PRIVATE_BUCKET_VIA_CLOUDFRONT=${ACCESS_PRIVATE_BUCKET_VIA_CLOUDFRONT} + - CDN_ENDPOINT=${CDN_ENDPOINT} + - CLOUDFRONT_KEY_PAIR_ID=${CLOUDFRONT_KEY_PAIR_ID} - CLOUDFRONT_PRIVATE_KEY=${CLOUDFRONT_PRIVATE_KEY} - CDN_MAX_AGE=${CDN_MAX_AGE} expose: - - 8000 + - 8000 + depends_on: + init: + condition: service_completed_successfully proxy: image: caddy @@ -34,4 +71,7 @@ services: volumes: - "./Caddyfile:/etc/caddy/Caddyfile" depends_on: - - medialit \ No newline at end of file + - medialit + +volumes: + medialit-postgres-data: diff --git a/docs/prds/mcp-server-prd.md b/docs/prds/mcp-server-prd.md index 5a5bf39b..7983ce4b 100644 --- a/docs/prds/mcp-server-prd.md +++ b/docs/prds/mcp-server-prd.md @@ -1,11 +1,17 @@ # MCP Server for MediaLit API **Issue:** #185 -**Status:** Implemented +**Status:** Superseded by the CodeLit Platform migration **Author:** Rajat Saxena **Date:** 2026-06-13 **Last revised:** 2026-06-17 +> **Superseded (2026-10-04).** MediaLit now uses Better Auth through +> `@codelitdev/oauth-server-kit` for OAuth and MCP discovery. The custom OAuth +> server described below (`src/oauth/`, the `/oauth/*` endpoints, self-signed +> JWTs, and `OAUTH_SIGNING_KEY`) has been removed. Keep this document for +> history only; it no longer describes the code. + > ## ✅ OAuth Restart-Safety Revision (2026-06-14) — implemented > > The original OAuth implementation in `src/oauth/` used **in-memory `Map` @@ -33,7 +39,7 @@ The MCP server uses **Streamable HTTP** transport — a single HTTP POST endpoin **Why Streamable HTTP:** -- No separate process to manage — one `pnpm dev` starts everything +- No separate process to manage — one `bun dev` starts everything - Reuses existing Express port, TLS certificate, and middleware stack - MCP clients connect via URL: `http://localhost:8000/mcp` (dev) or `https://api.medialit.cloud/mcp` (production) - Auth via both the existing `apikey` middleware (CLI/agent clients) and OAuth 2.0 (browser-based/ChatGPT clients) diff --git a/eslint.config.mjs b/eslint.config.mjs index c408c7b3..79c6e757 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,12 +1,45 @@ +import path from "node:path"; +import { createRequire } from "node:module"; import { defineConfig, globalIgnores } from "eslint/config"; import js from "@eslint/js"; import globals from "globals"; import tseslint from "typescript-eslint"; // import pluginReact from "eslint-plugin-react"; -import { FlatCompat } from '@eslint/eslintrc' +import { FlatCompat } from "@eslint/eslintrc"; + +const require = createRequire(import.meta.url); const compat = new FlatCompat({ baseDirectory: import.meta.dirname, -}) + resolvePluginsRelativeTo: path.dirname(require.resolve("eslint-config-next")), +}); + +// `eslint-config-next` parses JavaScript with `next/babel`. That preset resolves +// from the Next app, not the repo root, so `bun run lint` fails on every JS file. +// Keep the Next rules and let ESLint use its normal parser. +function withoutNextBabelParser(config) { + if (!config.languageOptions) return config; + const languageOptions = { ...config.languageOptions }; + delete languageOptions.parser; + if (languageOptions.parserOptions) { + const parserOptions = { ...languageOptions.parserOptions }; + delete parserOptions.babelOptions; + delete parserOptions.requireConfigFile; + delete parserOptions.allowImportExportEverywhere; + languageOptions.parserOptions = parserOptions; + } + return { ...config, languageOptions }; +} + +const nextConfigs = compat + .config({ + extends: ["next"], + settings: { + next: { + rootDir: "apps/web/", + }, + }, + }) + .map(withoutNextBabelParser); export default defineConfig([ @@ -15,23 +48,16 @@ export default defineConfig([ "**/dist", "**/components/ui/**", "**/.next/**", - ".migrations/**", "apps/docs/.source", "apps/docs/out/**", - "**/next-env.d.ts" + "**/next-env.d.ts", + "**/*.generated.ts" ]), { files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], plugins: { js }, extends: ["js/recommended"] }, { files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], languageOptions: { globals: { ...globals.browser, ...globals.node } } }, tseslint.configs.recommended, // pluginReact.configs.flat.recommended, - ...compat.config({ - extends: ['next'], - settings: { - next: { - rootDir: 'apps/web/', - }, - }, - }), + ...nextConfigs, { files: ["**/__tests__/**/*.{js,jsx,ts,tsx}", "**/*.{spec,test}.{js,jsx,ts,tsx}"], languageOptions: { diff --git a/examples/express/README.md b/examples/express/README.md index 69a0b8db..df30b71c 100644 --- a/examples/express/README.md +++ b/examples/express/README.md @@ -17,13 +17,13 @@ MEDIALIT_API_KEY=your_api_key_here 3. Install dependencies: ```bash -pnpm install +bun install ``` 4. Run: ```bash -pnpm dev +bun dev ``` ## Routes diff --git a/examples/next-app-router/AGENTS.md b/examples/next-app-router/AGENTS.md new file mode 100644 index 00000000..643577df --- /dev/null +++ b/examples/next-app-router/AGENTS.md @@ -0,0 +1,9 @@ + + +# This is NOT the Next.js you know + +This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices. + +This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean. + + diff --git a/examples/next-app-router/CLAUDE.md b/examples/next-app-router/CLAUDE.md new file mode 100644 index 00000000..43c994c2 --- /dev/null +++ b/examples/next-app-router/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/examples/next-app-router/README.md b/examples/next-app-router/README.md index b8c48fc4..4f976d0f 100644 --- a/examples/next-app-router/README.md +++ b/examples/next-app-router/README.md @@ -5,7 +5,7 @@ This is a [Next.js](https://nextjs.org) project which demonstrates the usage of This example app demonstrates two upload methods: 1. **Standard Upload** - Traditional single-request upload, best for smaller files -2. **TUS Resumable Upload** - Multipart resumable uploads using the TUS protocol with: +2. **TUS Resumable Upload** - The drop-in `MediaLitUploader` component from `@medialit/react`, with its signature route made by `createSignatureHandler` from `medialit`. It provides: - Real-time upload progress tracking - Automatic retry on failure - Cancel upload capability @@ -39,8 +39,6 @@ npm run dev # or yarn dev # or -pnpm dev -# or bun dev ``` diff --git a/examples/next-app-router/app/api/medialit/route.ts b/examples/next-app-router/app/api/medialit/route.ts index 632a0423..c458e46f 100644 --- a/examples/next-app-router/app/api/medialit/route.ts +++ b/examples/next-app-router/app/api/medialit/route.ts @@ -1,5 +1,5 @@ import { NextRequest } from "next/server"; -import { MediaLit } from "medialit"; +import { MediaLit, createSignatureHandler } from "medialit"; const client = new MediaLit(); @@ -35,26 +35,13 @@ export async function GET(request: NextRequest) { } } -export async function POST() { - try { - const signature = await client.getSignature(); - const sp = new URLSearchParams(); - sp.append("signature", signature); - return Response.json({ - endpoint: client.endpoint, - signature, - }); - } catch (error) { - if (error instanceof Error) { - console.log("Error getting presigned URL:", error); - return Response.json({ error: error.message }, { status: 500 }); - } - return Response.json( - { error: "An unknown error occurred" }, - { status: 500 }, - ); - } -} +// Returns { signature, endpoint } for uploads from the browser. +export const POST = createSignatureHandler({ + client, + // This demo has no sign-in, so anyone who can open it may upload. In your + // app, check the session here and return false for anonymous requests. + authorize: () => true, +}); export async function DELETE(request: NextRequest) { const searchParams = request.nextUrl.searchParams; diff --git a/examples/next-app-router/components/TusUploadForm.tsx b/examples/next-app-router/components/TusUploadForm.tsx index b7f620f5..a87f9dee 100644 --- a/examples/next-app-router/components/TusUploadForm.tsx +++ b/examples/next-app-router/components/TusUploadForm.tsx @@ -1,202 +1,23 @@ "use client"; -import { useState, useRef } from "react"; +import { useState } from "react"; import Image from "next/image"; -import { Upload } from "tus-js-client"; - -interface Media { - mediaId: string; - originalFileName: string; - mimeType: string; - size: number; - access: string; - file: string; - group?: string; - caption?: string; - thumbnail: string; -} +import { MediaLitUploader, type UploadedMedia } from "@medialit/react"; +import "@medialit/react/styles.css"; export default function TusUploadForm() { - const [file, setFile] = useState(null); - const [uploading, setUploading] = useState(false); - const [uploadedMedia, setUploadedMedia] = useState(null); - const [error, setError] = useState(""); const [caption, setCaption] = useState(""); const [isPublic, setIsPublic] = useState(false); - const [uploadProgress, setUploadProgress] = useState(0); - const [uploadSpeed, setUploadSpeed] = useState(""); + const [uploadedMedia, setUploadedMedia] = useState( + null, + ); + const [error, setError] = useState(""); const [sealing, setSealing] = useState(false); const [isSealed, setIsSealed] = useState(false); - const uploadRef = useRef(null); - const uploadedMediaFromHeaderRef = useRef(null); - - const handleUpload = async (e: React.FormEvent) => { - e.preventDefault(); - if (!file) return; - - setUploading(true); - setError(""); - setUploadProgress(0); - setUploadSpeed(""); - setIsSealed(false); - uploadedMediaFromHeaderRef.current = null; - - try { - // Get presigned URL - const presignedUrlResponse = await fetch("/api/medialit", { - method: "POST", - }); - const { endpoint, signature, error } = - await presignedUrlResponse.json(); - - if (error || !signature) { - throw new Error(error || "Failed to get signature"); - } - - // Use the endpoint directly since we're sending signature in headers - const uploadUrl = `${endpoint}/media/create/resumable`; - - // Prepare metadata for tus (tus-js-client will encode values as base64) - const metadata = { - fileName: file.name, - mimeType: file.type, - access: isPublic ? "public" : "private", - caption: caption || "", - }; - - // Create tus upload - const upload = new Upload(file, { - endpoint: uploadUrl, - chunkSize: 1024000, - retryDelays: [0, 3000, 5000], - headers: { - "x-medialit-signature": signature, - }, - metadata, - onAfterResponse: (_req, res) => { - const mediaHeader = res.getHeader("media"); - if (!mediaHeader) return; - - try { - const parsed = JSON.parse(mediaHeader); - if (parsed?.mediaId) { - uploadedMediaFromHeaderRef.current = - parsed as Media; - } - } catch { - // no-op: fallback flow below will handle missing/invalid header - } - }, - onError: (error) => { - console.error("Tus upload error:", error); - setError( - error instanceof Error - ? error.message - : "Upload failed", - ); - setUploading(false); - }, - onProgress: (bytesUploaded, bytesTotal) => { - const percentage = (bytesUploaded / bytesTotal) * 100; - setUploadProgress(percentage); - }, - onSuccess: async () => { - setUploading(false); - setUploadProgress(100); - // Prefer the finalized media object returned by backend in TUS response headers. - if (uploadedMediaFromHeaderRef.current?.mediaId) { - setUploadedMedia(uploadedMediaFromHeaderRef.current); - setIsSealed(false); - } else { - const uploadUrl = upload.url; - const mediaId = uploadUrl?.split("/").pop(); - - // Fallback: attempt to resolve by id (may fail if id is not mediaId). - if (mediaId) { - try { - const mediaResponse = await fetch( - `/api/medialit?mediaId=${mediaId}`, - ); - const mediaData = await mediaResponse.json(); - - if (mediaResponse.ok) { - setUploadedMedia(mediaData); - setIsSealed(false); - } else { - setUploadedMedia({ - mediaId, - originalFileName: file.name, - mimeType: file.type, - size: file.size, - access: isPublic ? "public" : "private", - file: "", - caption: caption || "", - thumbnail: "", - }); - setIsSealed(false); - } - } catch { - setUploadedMedia({ - mediaId, - originalFileName: file.name, - mimeType: file.type, - size: file.size, - access: isPublic ? "public" : "private", - file: "", - caption: caption || "", - thumbnail: "", - }); - setIsSealed(false); - } - } else { - setUploadedMedia({ - mediaId: "", - originalFileName: file.name, - mimeType: file.type, - size: file.size, - access: isPublic ? "public" : "private", - file: "", - caption: caption || "", - thumbnail: "", - }); - setIsSealed(false); - } - } - - // Reset form after 3 seconds - setTimeout(() => { - setFile(null); - setCaption(""); - }, 3000); - }, - }); - - uploadRef.current = upload; - upload.start(); - } catch (err) { - setError( - err instanceof Error - ? err.message - : "An error occurred during upload", - ); - setUploading(false); - } - }; - - const handleCancel = () => { - if (uploadRef.current) { - uploadRef.current.abort(); - uploadRef.current = null; - setUploading(false); - setUploadProgress(0); - } - }; + // Uploads stay temporary until your backend seals them. const handleSeal = async () => { - if (!uploadedMedia?.mediaId) { - setError("Unable to resolve media ID for sealing."); - return; - } + if (!uploadedMedia) return; setSealing(true); setError(""); @@ -226,91 +47,47 @@ export default function TusUploadForm() { }; return ( -
-
-
- - setFile(e.target.files?.[0] || null)} - className="border rounded-md p-2" - /> -
- -
- - setCaption(e.target.value)} - className="border rounded-md p-2" - /> -
- -
- setIsPublic(e.target.checked)} - className="rounded" - /> - -
- - - - {uploading && ( - - )} -
- - {uploading && ( -
-
-
-
-

- {uploadProgress.toFixed(1)}% uploaded -

- {uploadSpeed && ( -

{uploadSpeed}

- )} -
- )} +
+
+ + setCaption(e.target.value)} + className="border rounded-md p-2" + /> +
+ +
+ setIsPublic(e.target.checked)} + className="rounded" + /> + +
+ + { + setUploadedMedia(media); + setIsSealed(false); + setError(""); + }} + /> {error && ( -
+
{error}
)} @@ -321,7 +98,7 @@ export default function TusUploadForm() { Uploaded File Details - {uploadedMedia.file && ( + {uploadedMedia.mimeType.startsWith("image/") && (

Media ID:{" "} - {uploadedMedia.mediaId || "Pending"} + {uploadedMedia.mediaId}

File name:{" "} @@ -358,27 +135,25 @@ export default function TusUploadForm() { Caption:{" "} {uploadedMedia.caption}

- {uploadedMedia.file ? ( - - Direct link - - ) : null} + + Direct link +
- {!isSealed ? ( + {!isSealed && ( - ) : null} + )}
)}
diff --git a/examples/next-app-router/package.json b/examples/next-app-router/package.json index d45db10e..f3a06523 100644 --- a/examples/next-app-router/package.json +++ b/examples/next-app-router/package.json @@ -6,15 +6,15 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "eslint ." }, "dependencies": { + "@medialit/react": "workspace:^", "lucide-react": "^0.487.0", "medialit": "workspace:^", - "next": "15.5.7", + "next": "16.3.6", "react": "^19.0.0", - "react-dom": "^19.0.0", - "tus-js-client": "^4.3.1" + "react-dom": "^19.0.0" }, "devDependencies": { "@eslint/eslintrc": "^3", diff --git a/examples/next-app-router/tsconfig.json b/examples/next-app-router/tsconfig.json index d8b93235..e7ff3a26 100644 --- a/examples/next-app-router/tsconfig.json +++ b/examples/next-app-router/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -11,7 +15,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve", + "jsx": "react-jsx", "incremental": true, "plugins": [ { @@ -19,9 +23,19 @@ } ], "paths": { - "@/*": ["./*"] + "@/*": [ + "./*" + ] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] } diff --git a/package.json b/package.json index 6edb55bb..11da14ec 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "@eslint/js": "^9.24.0", "@next/eslint-plugin-next": "^15.2.4", "@typescript-eslint/eslint-plugin": "^8.29.1", - "eslint": "^9.24.0", + "eslint": "^9.39.5", + "eslint-config-next": "15.2.4", "eslint-plugin-react": "^7.37.5", "globals": "^16.0.0", "husky": "^9.1.7", @@ -18,17 +19,73 @@ }, "scripts": { "prepare": "husky", - "ci:publish": "pnpm publish -r", - "test": "pnpm --filter @medialit/api --filter medialit test", + "ci:publish": "bunx changeset publish", + "test": "bun run --filter @medialit/api test && bun run --filter medialit test && bun run --filter @medialit/uploader test && bun run --filter @medialit/react test && bun run --filter @medialit/cli test", "lint": "eslint --cache --quiet", "prettier": "prettier --check **/*.{ts,tsx,js,css,md}", "lint:fix": "eslint --cache --fix", "prettier:fix": "prettier --write **/*.{ts,tsx,js,css,md}", - "release": "./release.sh" + "release": "./release.sh", + "dev:api": "bun --filter @medialit/api dev", + "dev:web": "bun --filter @medialit/web dev --port 3007", + "dev:docs": "bun --filter @medialit/docs dev --port 3008", + "test:integration": "bun --filter @medialit/integration-tests test:integration", + "test:integration:stack": "bash packages/integration-tests/scripts/run-with-local-stack.sh" }, "lint-staged": { - "*.{ts,tsx}": "eslint --cache --fix **/*.{ts,tsx}", + "*.{ts,tsx}": "eslint --cache --fix", "*.{ts,tsx,js,css,md}": "prettier --write" }, - "packageManager": "pnpm@10.7.1+sha512.2d92c86b7928dc8284f53494fb4201f983da65f0fb4f0d40baafa5cf628fa31dae3e5968f12466f17df7e97310e30f343a648baea1b9b350685dafafffdf5808" -} \ No newline at end of file + "overrides": { + "@babel/core@>=7.0.0 <8.0.0": "7.29.6", + "@eslint/plugin-kit@>=0.2.0 <0.3.0": "0.3.5", + "@hono/node-server@>=1.0.0 <2.0.0": "1.19.15", + "@humanfs/node@>=0.16.0 <0.17.0": "0.16.8", + "ajv@>=6.0.0 <7.0.0": "6.14.0", + "baseline-browser-mapping@>=2.0.0 <3.0.0": "2.11.0", + "body-parser@>=1.0.0 <2.0.0": "1.20.6", + "body-parser@>=2.0.0 <3.0.0": "2.3.0", + "brace-expansion@>=1.0.0 <2.0.0": "1.1.21", + "brace-expansion@>=2.0.0 <3.0.0": "2.1.7", + "browserslist@>=4.0.0 <5.0.0": "4.28.7", + "diff@>=4.0.0 <5.0.0": "4.0.4", + "fast-uri@>=3.0.0 <4.0.0": "3.1.8", + "fast-xml-builder@>=1.0.0 <2.0.0": "1.1.7", + "fast-xml-parser@>=5.0.0 <6.0.0": "5.7.0", + "flatted@>=3.0.0 <4.0.0": "3.4.2", + "form-data@>=2.0.0 <3.0.0": "2.5.6", + "form-data@>=4.0.0 <5.0.0": "4.0.6", + "glob@>=10.0.0 <11.0.0": "10.5.0", + "hono@>=4.0.0 <5.0.0": "4.13.7", + "image-size@>=2.0.0 <3.0.0": "2.0.3", + "ip-address@>=10.0.0 <11.0.0": "10.7.1", + "joi@>=17.0.0 <18.0.0": "17.13.7", + "js-yaml@>=3.0.0 <4.0.0": "3.15.2", + "js-yaml@>=4.0.0 <5.0.0": "4.3.2", + "jws@>=3.0.0 <4.0.0": "3.2.3", + "lodash@>=4.0.0 <5.0.0": "4.18.0", + "mdast-util-to-hast@>=13.0.0 <14.0.0": "13.2.1", + "minimatch@>=3.0.0 <4.0.0": "3.1.4", + "minimatch@>=9.0.0 <10.0.0": "9.0.7", + "nanoid@>=3.0.0 <4.0.0": "3.3.18", + "next@>=16.0.0 <17.0.0": "16.3.6", + "path-to-regexp@>=0.1.0 <0.2.0": "0.1.13", + "path-to-regexp@>=8.0.0 <9.0.0": "8.4.0", + "picomatch@>=2.0.0 <3.0.0": "2.3.2", + "picomatch@>=4.0.0 <5.0.0": "4.0.4", + "postcss@>=8.0.0 <9.0.0": "8.5.23", + "postcss-selector-parser@>=6.0.0 <7.0.0": "6.1.3", + "postcss-selector-parser@>=7.0.0 <8.0.0": "7.1.3", + "qs@>=6.0.0 <7.0.0": "6.16.0", + "rollup@>=4.0.0 <5.0.0": "4.59.0", + "tmp@>=0.0.0 <1.0.0": "0.2.7", + "ws@>=5.0.0 <6.0.0": "5.2.5", + "yaml@>=2.0.0 <3.0.0": "2.8.3" + }, + "packageManager": "bun@1.4.1", + "workspaces": [ + "apps/*", + "packages/*", + "examples/*" + ] +} diff --git a/packages/cli/AGENTS.md b/packages/cli/AGENTS.md new file mode 100644 index 00000000..d5fded30 --- /dev/null +++ b/packages/cli/AGENTS.md @@ -0,0 +1,3 @@ +## Development Tips + +- When adding, removing, renaming, or changing a command or flag, update `packages/integration-tests/src/cli.ts`. Keep its expected command inventory exact and exercise every command's inputs, outputs, and behavior, including cleanup for any test data. Run it against a live API when credentials and a test account are available with `bun --filter @medialit/integration-tests test:cli` (it builds the CLI first). diff --git a/packages/cli/README.md b/packages/cli/README.md new file mode 100644 index 00000000..7e414496 --- /dev/null +++ b/packages/cli/README.md @@ -0,0 +1,53 @@ +# @medialit/cli + +Upload and manage [MediaLit](https://medialit.cloud) files from the terminal. + +```bash +npm install -g @medialit/cli +medialit login +medialit upload photo.jpg --public +``` + +## Log in + +```bash +medialit login +``` + +Your browser opens to sign in. If you have more than one app, you choose the app the CLI works with. Run `medialit login` again to choose a different one. + +For a self-hosted MediaLit, pass its URL once. Later commands use the server you last logged in to: + +```bash +medialit login --endpoint https://medialit.example.com +``` + +Without a browser, for example over SSH, use `--no-browser` and open the printed URL on another device whose browser can reach this machine's `127.0.0.1`. + +In CI, skip logging in and set `MEDIALIT_API_KEY` (and `MEDIALIT_ENDPOINT` if you self-host). + +## Commands + +| Command | What it does | +| -------------------------- | -------------------------------------------------- | +| `medialit upload ` | Uploads and seals files, then prints their URLs | +| `medialit ls` | Lists sealed files, newest first | +| `medialit get ` | Prints a file's URL, a fresh one for private files | +| `medialit seal ` | Keeps a temporary upload | +| `medialit rm ` | Deletes a file | +| `medialit whoami` | Shows the server, account, app and storage used | +| `medialit logout` | Logs out of the server | + +Uploads are private unless you pass `--public`. Add `--caption ` or `--group ` to label them, or `--temp` to leave them unsealed so MediaLit deletes them after 24 hours. Large files are uploaded in chunks and retried if the connection drops. + +`ls` takes `--public`, `--private`, `--group `, `--page ` and `--limit `. + +Every command takes `--json`. URLs and JSON go to stdout and messages to stderr, so you can pipe the output: + +```bash +medialit upload report.pdf --public | pbcopy +``` + +## Where your login is stored + +In `~/.config/medialit/credentials.json` (`%APPDATA%\medialit` on Windows), readable only by you. Set `MEDIALIT_CONFIG_DIR` to use another folder. diff --git a/packages/cli/__tests__/oauth.test.ts b/packages/cli/__tests__/oauth.test.ts new file mode 100644 index 00000000..044add6b --- /dev/null +++ b/packages/cli/__tests__/oauth.test.ts @@ -0,0 +1,217 @@ +import { describe, test, before, after, beforeEach } from "node:test"; +import assert from "node:assert"; +import { createHash } from "node:crypto"; +import http from "node:http"; +import { mkdtemp, rm, stat } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import type { AddressInfo } from "node:net"; +import { authorize, discover, refresh, register } from "../src/oauth"; +import { loadCredentials, saveCredentials } from "../src/credentials"; +import { getClient } from "../src/session"; + +const jwt = (claims: object) => + `x.${Buffer.from(JSON.stringify(claims)).toString("base64url")}.x`; + +// Speaks the parts of OAuth that MediaLit's server uses for MCP clients. +async function startFakeServer() { + const state = { + registrations: [] as any[], + tokenRequests: [] as URLSearchParams[], + challenge: "", + apiAuthorization: "", + }; + const server = http.createServer(async (req, res) => { + const url = new URL(req.url!, base); + let body = ""; + for await (const chunk of req) body += chunk; + const json = (data: object, status = 200) => { + res.writeHead(status, { "content-type": "application/json" }); + res.end(JSON.stringify(data)); + }; + + if (url.pathname === "/.well-known/oauth-protected-resource") { + return json({ + resource: `${base}/mcp`, + authorization_servers: [`${base}/api/auth`], + }); + } + if ( + url.pathname === "/.well-known/oauth-authorization-server/api/auth" + ) { + return json({ + authorization_endpoint: `${base}/api/auth/oauth2/authorize`, + token_endpoint: `${base}/api/auth/oauth2/token`, + registration_endpoint: `${base}/api/auth/oauth2/register`, + revocation_endpoint: `${base}/api/auth/oauth2/revoke`, + userinfo_endpoint: `${base}/api/auth/oauth2/userinfo`, + }); + } + if (url.pathname === "/api/auth/oauth2/register") { + state.registrations.push(JSON.parse(body)); + return json({ client_id: "client-1" }, 201); + } + if (url.pathname === "/api/auth/oauth2/authorize") { + // Signing in and picking an app happen here in the real server. + state.challenge = url.searchParams.get("code_challenge")!; + const redirect = new URL(url.searchParams.get("redirect_uri")!); + redirect.searchParams.set("code", "code-1"); + redirect.searchParams.set("state", url.searchParams.get("state")!); + res.writeHead(302, { location: redirect.toString() }); + return res.end(); + } + if (url.pathname === "/api/auth/oauth2/token") { + const params = new URLSearchParams(body); + state.tokenRequests.push(params); + if (params.get("grant_type") === "authorization_code") { + const challenge = createHash("sha256") + .update(params.get("code_verifier")!) + .digest("base64url"); + if (challenge !== state.challenge) { + return json({ error: "invalid_grant" }, 400); + } + } + return json({ + access_token: jwt({ app_id: "app-7" }), + refresh_token: "refresh-2", + expires_in: 3600, + }); + } + if (url.pathname === "/api/auth/oauth2/userinfo") { + return json({ email: "ada@example.com" }); + } + if (url.pathname === "/media/get/count") { + state.apiAuthorization = req.headers.authorization || ""; + return json({ count: 4 }); + } + res.writeHead(404).end(); + }); + await new Promise((resolve) => + server.listen(0, "127.0.0.1", resolve), + ); + const base = `http://127.0.0.1:${(server.address() as AddressInfo).port}`; + return { + ...state, + get state() { + return state; + }, + base, + close: () => new Promise((r) => server.close(() => r())), + }; +} + +describe("login", () => { + let fake: Awaited>; + let configDir: string; + + before(async () => { + fake = await startFakeServer(); + }); + after(() => fake.close()); + beforeEach(async () => { + configDir = await mkdtemp(join(tmpdir(), "medialit-cli-")); + process.env.MEDIALIT_CONFIG_DIR = configDir; + delete process.env.MEDIALIT_API_KEY; + delete process.env.MEDIALIT_ENDPOINT; + }); + + test("discovers, registers and completes the PKCE flow", async () => { + const metadata = await discover(fake.base); + assert.strictEqual(metadata.resource, `${fake.base}/mcp`); + + const clientId = await register(metadata); + assert.strictEqual(clientId, "client-1"); + const registration = fake.state.registrations.at(-1); + assert.strictEqual(registration.application_type, "native"); + assert.deepStrictEqual(registration.redirect_uris, [ + "http://127.0.0.1/callback", + ]); + + // Stands in for the browser: follows the redirect to the CLI. + const tokens = await authorize(metadata, clientId, (url) => { + fetch(url); + }); + assert.strictEqual(tokens.email, "ada@example.com"); + assert.strictEqual(tokens.appId, "app-7"); + assert.strictEqual(tokens.refreshToken, "refresh-2"); + + const exchange = fake.state.tokenRequests.at(-1)!; + assert.strictEqual(exchange.get("resource"), `${fake.base}/mcp`); + assert.match( + exchange.get("redirect_uri")!, + /^http:\/\/127\.0\.0\.1:\d+\/callback$/, + ); + }); + + test("rejects a callback with an error", async () => { + const metadata = await discover(fake.base); + await assert.rejects( + authorize(metadata, "client-1", (url) => { + const redirect = new URL( + new URL(url).searchParams.get("redirect_uri")!, + ); + redirect.searchParams.set("error", "access_denied"); + fetch(redirect); + }), + /access_denied/, + ); + }); + + test("refreshes an expiring login before calling the API", async () => { + await saveCredentials({ + current: fake.base, + servers: { + [fake.base]: { + clientId: "client-1", + tokenEndpoint: `${fake.base}/api/auth/oauth2/token`, + resource: `${fake.base}/mcp`, + accessToken: "old", + refreshToken: "refresh-1", + expiresAt: Date.now() + 1000, + }, + }, + }); + + const { client, endpoint } = await getClient(); + assert.strictEqual(endpoint, fake.base); + assert.strictEqual(await client.getCount(), 4); + + const refreshed = fake.state.tokenRequests.at(-1)!; + assert.strictEqual(refreshed.get("grant_type"), "refresh_token"); + assert.strictEqual(refreshed.get("refresh_token"), "refresh-1"); + assert.strictEqual( + fake.state.apiAuthorization, + `Bearer ${jwt({ app_id: "app-7" })}`, + ); + + const saved = (await loadCredentials()).servers[fake.base]; + assert.strictEqual(saved.refreshToken, "refresh-2"); + if (process.platform !== "win32") { + const mode = (await stat(join(configDir, "credentials.json"))).mode; + assert.strictEqual(mode & 0o777, 0o600); + } + await rm(configDir, { recursive: true }); + }); + + test("uses MEDIALIT_API_KEY without a login", async () => { + process.env.MEDIALIT_API_KEY = "key-1"; + const { client } = await getClient(fake.base); + assert.strictEqual(await client.getCount(), 4); + delete process.env.MEDIALIT_API_KEY; + }); + + test("explains how to log in", async () => { + await assert.rejects(getClient(fake.base), /medialit login/); + }); + + test("refresh surfaces the server's error", async () => { + await assert.rejects( + refresh(`${fake.base}/nope`, { + clientId: "c", + refreshToken: "r", + resource: "x", + }), + /404/, + ); + }); +}); diff --git a/packages/cli/package.json b/packages/cli/package.json new file mode 100644 index 00000000..3417de4d --- /dev/null +++ b/packages/cli/package.json @@ -0,0 +1,47 @@ +{ + "name": "@medialit/cli", + "version": "0.1.0", + "description": "Upload and manage MediaLit files from the terminal", + "type": "module", + "bin": { + "medialit": "dist/index.mjs" + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsup", + "test": "bun --filter medialit --filter @medialit/uploader build && node --import tsx --test '__tests__/**/*.test.ts'", + "prepublishOnly": "bun run build" + }, + "keywords": [ + "medialit", + "cli", + "upload", + "storage" + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/codelitdev/medialit" + }, + "dependencies": { + "@medialit/uploader": "workspace:^", + "medialit": "workspace:^", + "mime-types": "^3.0.2" + }, + "devDependencies": { + "@types/mime-types": "^3.0.1", + "@types/node": "^20.0.0", + "tsup": "^8.0.0", + "tsx": "^4.19.3", + "typescript": "^5.0.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "publishConfig": { + "access": "public" + }, + "packageManager": "bun@1.4.1" +} diff --git a/packages/cli/src/commands.ts b/packages/cli/src/commands.ts new file mode 100644 index 00000000..72a40721 --- /dev/null +++ b/packages/cli/src/commands.ts @@ -0,0 +1,268 @@ +import { spawn } from "node:child_process"; +import { createReadStream } from "node:fs"; +import { stat } from "node:fs/promises"; +import { basename } from "node:path"; +import { lookup } from "mime-types"; +import { uploadFile, type UploadedMedia } from "@medialit/uploader"; +import { loadCredentials, saveCredentials } from "./credentials"; +import { authorize, discover, register, revoke } from "./oauth"; +import { getClient, resolveEndpoint } from "./session"; + +export interface Flags { + endpoint?: string; + json?: boolean; + public?: boolean; + private?: boolean; + caption?: string; + group?: string; + temp?: boolean; + page?: string; + limit?: string; + browser?: boolean; +} + +export async function login(flags: Flags): Promise { + const endpoint = await resolveEndpoint(flags.endpoint); + const metadata = await discover(endpoint); + const credentials = await loadCredentials(); + const previous = credentials.servers[endpoint]; + + const clientId = previous?.clientId || (await register(metadata)); + const tokens = await authorize(metadata, clientId, (url) => { + log(`Opening your browser to log in to ${endpoint}.`); + log(`If it doesn't open, visit:\n\n ${url}\n`); + if (flags.browser !== false) openBrowser(url); + }); + + credentials.servers[endpoint] = { + clientId, + tokenEndpoint: metadata.tokenEndpoint, + revocationEndpoint: metadata.revocationEndpoint, + resource: metadata.resource, + ...tokens, + }; + credentials.current = endpoint; + await saveCredentials(credentials); + + output(flags, { endpoint, email: tokens.email, appId: tokens.appId }, () => + [ + `Logged in${tokens.email ? ` as ${tokens.email}` : ""}.`, + tokens.appId ? `Uploads go to app ${tokens.appId}.` : "", + "Run `medialit login` again to choose a different app.", + ] + .filter(Boolean) + .join("\n"), + ); +} + +export async function logout(flags: Flags): Promise { + const endpoint = await resolveEndpoint(flags.endpoint); + const credentials = await loadCredentials(); + const server = credentials.servers[endpoint]; + if (server?.refreshToken && server.revocationEndpoint) { + await revoke(server.revocationEndpoint, { + clientId: server.clientId, + token: server.refreshToken, + }).catch(() => undefined); + } + if (server) { + // Keep the registration so the next login doesn't register again. + credentials.servers[endpoint] = { + clientId: server.clientId, + tokenEndpoint: server.tokenEndpoint, + revocationEndpoint: server.revocationEndpoint, + resource: server.resource, + }; + await saveCredentials(credentials); + } + output(flags, { endpoint }, () => `Logged out of ${endpoint}.`); +} + +export async function whoami(flags: Flags): Promise { + const { client, endpoint } = await getClient(flags.endpoint); + const server = (await loadCredentials()).servers[endpoint]; + const usingApiKey = !!process.env.MEDIALIT_API_KEY; + const [stats, count] = await Promise.all([ + client.getStats(), + client.getCount(), + ]); + const info = { + endpoint, + auth: usingApiKey ? "api-key" : "login", + email: usingApiKey ? undefined : server?.email, + appId: usingApiKey ? undefined : server?.appId, + files: count, + storage: stats.storage, + maxStorage: stats.maxStorage, + }; + output(flags, info, () => + [ + `Server: ${endpoint}`, + usingApiKey + ? "Using: MEDIALIT_API_KEY" + : `Account: ${info.email || "unknown"}`, + info.appId ? `App: ${info.appId}` : "", + `Files: ${count}`, + `Storage: ${formatBytes(stats.storage)} of ${formatBytes(stats.maxStorage)}`, + ] + .filter(Boolean) + .join("\n"), + ); +} + +export async function upload(paths: string[], flags: Flags): Promise { + if (!paths.length) throw new Error("Pass at least one file to upload."); + const { client, endpoint } = await getClient(flags.endpoint); + const results: UploadedMedia[] = []; + + for (const path of paths) { + const info = await stat(path).catch(() => null); + if (!info?.isFile()) throw new Error(`${path} is not a file`); + + const fileName = basename(path); + const media = await uploadFile(createReadStream(path), { + getSignature: async () => ({ + signature: await client.getSignature({ group: flags.group }), + endpoint, + }), + fileName, + mimeType: lookup(fileName) || undefined, + access: flags.public ? "public" : "private", + caption: flags.caption, + // Chunks let a dropped connection resume instead of restarting. + chunkSize: 8 * 1024 * 1024, + onProgress: ({ percentage }) => + progress(`${fileName} ${Math.floor(percentage)}%`), + }); + progress(""); + + const kept = flags.temp + ? media + : ((await client.seal(media.mediaId)) as unknown as UploadedMedia); + results.push(kept); + if (!flags.json) { + log(`${kept.originalFileName} ${kept.mediaId}`); + console.log(kept.file); + } + } + + if (flags.json) console.log(JSON.stringify(results, null, 2)); + else if (flags.temp) { + log( + "\nThese uploads are temporary. Seal them with `medialit seal ` or they are deleted after 24 hours.", + ); + } +} + +export async function list(flags: Flags): Promise { + const { client } = await getClient(flags.endpoint); + const access = flags.public + ? "public" + : flags.private + ? "private" + : undefined; + const media = (await client.list( + Number(flags.page || 1), + Number(flags.limit || 20), + { access, group: flags.group }, + )) as unknown as UploadedMedia[]; + + output(flags, media, () => + media.length + ? table( + media.map((m) => [ + m.mediaId, + m.access, + formatBytes(m.size), + m.originalFileName, + ]), + ) + : "No files.", + ); +} + +export async function get(ids: string[], flags: Flags): Promise { + const { client } = await getClient(flags.endpoint); + const media = (await client.get( + requireId(ids), + )) as unknown as UploadedMedia; + output(flags, media, () => media.file); +} + +export async function seal(ids: string[], flags: Flags): Promise { + const { client } = await getClient(flags.endpoint); + const media = (await client.seal( + requireId(ids), + )) as unknown as UploadedMedia; + output(flags, media, () => media.file); +} + +export async function remove(ids: string[], flags: Flags): Promise { + const { client } = await getClient(flags.endpoint); + const id = requireId(ids); + await client.delete(id); + output(flags, { deleted: true, mediaId: id }, () => `Deleted ${id}.`); +} + +function requireId(ids: string[]): string { + if (ids.length !== 1) throw new Error("Pass one media ID."); + return ids[0]; +} + +function output(flags: Flags, data: unknown, human: () => string) { + console.log(flags.json ? JSON.stringify(data, null, 2) : human()); +} + +/** Messages go to stderr so stdout stays clean for piping. */ +function log(message: string) { + console.error(message); +} + +function progress(message: string) { + if (!process.stderr.isTTY) return; + process.stderr.write(`\r\x1b[2K${message}`); +} + +function table(rows: string[][]): string { + const widths = rows[0].map((_, i) => + Math.max(...rows.map((row) => row[i].length)), + ); + return rows + .map((row) => + row + .map((cell, i) => + i === row.length - 1 ? cell : cell.padEnd(widths[i]), + ) + .join(" "), + ) + .join("\n"); +} + +export function formatBytes(bytes: number): string { + const units = ["B", "KB", "MB", "GB", "TB"]; + let value = bytes; + let unit = 0; + while (value >= 1024 && unit < units.length - 1) { + value /= 1024; + unit++; + } + const rounded = + value >= 10 || unit === 0 ? Math.round(value) : value.toFixed(1); + return `${rounded} ${units[unit]}`; +} + +function openBrowser(url: string) { + const [command, args] = + process.platform === "darwin" + ? ["open", [url]] + : process.platform === "win32" + ? ["rundll32", ["url.dll,FileProtocolHandler", url]] + : ["xdg-open", [url]]; + try { + spawn(command, args, { stdio: "ignore", detached: true }) + .on("error", () => undefined) + .unref(); + } catch { + // The URL is printed, so the user can open it themselves. + } +} diff --git a/packages/cli/src/credentials.ts b/packages/cli/src/credentials.ts new file mode 100644 index 00000000..42551b76 --- /dev/null +++ b/packages/cli/src/credentials.ts @@ -0,0 +1,64 @@ +import { chmod, mkdir, readFile, writeFile } from "node:fs/promises"; +import { homedir } from "node:os"; +import { join } from "node:path"; + +export const DEFAULT_ENDPOINT = "https://api.medialit.cloud"; + +/** What the CLI remembers about one MediaLit server. */ +export interface ServerCredentials { + /** From registering the CLI with the server. Kept after logout. */ + clientId: string; + tokenEndpoint: string; + revocationEndpoint?: string; + resource: string; + accessToken?: string; + refreshToken?: string; + /** Milliseconds since the epoch. */ + expiresAt?: number; + email?: string; + appId?: string; +} + +export interface Credentials { + /** The server commands use when no endpoint is given. */ + current?: string; + servers: Record; +} + +export function normalizeEndpoint(endpoint: string): string { + return new URL(endpoint).toString().replace(/\/+$/, ""); +} + +export function configDir(): string { + if (process.env.MEDIALIT_CONFIG_DIR) return process.env.MEDIALIT_CONFIG_DIR; + if (process.platform === "win32") { + return join( + process.env.APPDATA || join(homedir(), "AppData", "Roaming"), + "medialit", + ); + } + return join( + process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), + "medialit", + ); +} + +const credentialsFile = () => join(configDir(), "credentials.json"); + +export async function loadCredentials(): Promise { + try { + const parsed = JSON.parse(await readFile(credentialsFile(), "utf8")); + return { servers: {}, ...parsed }; + } catch { + return { servers: {} }; + } +} + +export async function saveCredentials(credentials: Credentials): Promise { + await mkdir(configDir(), { recursive: true, mode: 0o700 }); + // The file holds tokens, so only the user may read it. + await writeFile(credentialsFile(), JSON.stringify(credentials, null, 2), { + mode: 0o600, + }); + await chmod(credentialsFile(), 0o600); +} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts new file mode 100644 index 00000000..af886ec9 --- /dev/null +++ b/packages/cli/src/index.ts @@ -0,0 +1,101 @@ +import { parseArgs } from "node:util"; +import { createRequire } from "node:module"; +import * as commands from "./commands"; + +const HELP = `Usage: medialit [options] + +Commands: + login Log in with your browser and choose an app + logout Log out of the server + whoami Show the server, account, app and storage used + upload Upload and seal files, then print their URLs + ls List sealed files, newest first + get Print a file's URL (a fresh one for private files) + seal Keep a temporary upload + rm Delete a file + +Options: + --endpoint MediaLit server, for self-hosted instances + (default: the server you last logged in to, + or https://api.medialit.cloud) + --json Print JSON + --public upload: make files public. ls: only public files + --private ls: only private files + --caption upload: caption for the files + --group upload: put files in a group. ls: only that group + --temp upload: don't seal, so unused files are deleted + --page , --limit ls: page through files + --no-browser login: print the URL instead of opening it + -h, --help Show this help + -v, --version Show the version + +Environment: + MEDIALIT_API_KEY Use an API key instead of logging in, for CI + MEDIALIT_ENDPOINT Same as --endpoint +`; + +async function main(argv: string[]): Promise { + const { values, positionals } = parseArgs({ + args: argv, + allowPositionals: true, + options: { + endpoint: { type: "string" }, + json: { type: "boolean" }, + public: { type: "boolean" }, + private: { type: "boolean" }, + caption: { type: "string" }, + group: { type: "string" }, + temp: { type: "boolean" }, + page: { type: "string" }, + limit: { type: "string" }, + "no-browser": { type: "boolean" }, + help: { type: "boolean", short: "h" }, + version: { type: "boolean", short: "v" }, + }, + }); + const flags: commands.Flags = { + ...values, + browser: !values["no-browser"], + }; + const [command, ...args] = positionals; + + if (values.version) { + const require = createRequire(import.meta.url); + console.log(require("../package.json").version); + return; + } + if (values.help || !command) { + console.log(HELP); + return; + } + + switch (command) { + case "login": + return commands.login(flags); + case "logout": + return commands.logout(flags); + case "whoami": + return commands.whoami(flags); + case "upload": + return commands.upload(args, flags); + case "ls": + case "list": + return commands.list(flags); + case "get": + return commands.get(args, flags); + case "seal": + return commands.seal(args, flags); + case "rm": + case "delete": + return commands.remove(args, flags); + default: + throw new Error( + `Unknown command "${command}". Run \`medialit --help\`.`, + ); + } +} + +main(process.argv.slice(2)).catch((err: Error) => { + console.error(`error: ${err.message}`); + process.exitCode = 1; +}); diff --git a/packages/cli/src/oauth.ts b/packages/cli/src/oauth.ts new file mode 100644 index 00000000..aa7d22a3 --- /dev/null +++ b/packages/cli/src/oauth.ts @@ -0,0 +1,283 @@ +import { createHash, randomBytes } from "node:crypto"; +import http from "node:http"; +import type { AddressInfo } from "node:net"; + +export const SCOPES = "openid email offline_access data:read data:write"; +const CALLBACK_PATH = "/callback"; +const LOGIN_TIMEOUT_MS = 5 * 60 * 1000; + +export interface ServerMetadata { + authorizationEndpoint: string; + tokenEndpoint: string; + registrationEndpoint?: string; + revocationEndpoint?: string; + userinfoEndpoint?: string; + /** The audience to request tokens for. */ + resource: string; +} + +export interface Tokens { + accessToken: string; + refreshToken?: string; + expiresAt?: number; + email?: string; + appId?: string; +} + +/** Finds the server's OAuth endpoints the same way MCP clients do. */ +export async function discover(endpoint: string): Promise { + const resource = await getJson( + `${endpoint}/.well-known/oauth-protected-resource`, + ); + const issuer = resource.authorization_servers?.[0]; + if (!issuer) { + throw new Error(`${endpoint} does not support logging in with OAuth`); + } + // RFC 8414: the issuer's path goes after the well-known segment. + const issuerUrl = new URL(issuer); + const path = issuerUrl.pathname === "/" ? "" : issuerUrl.pathname; + const server = await getJson( + `${issuerUrl.origin}/.well-known/oauth-authorization-server${path}`, + ); + return { + authorizationEndpoint: server.authorization_endpoint, + tokenEndpoint: server.token_endpoint, + registrationEndpoint: server.registration_endpoint, + revocationEndpoint: server.revocation_endpoint, + userinfoEndpoint: server.userinfo_endpoint, + resource: resource.resource, + }; +} + +/** Registers the CLI as a native app that redirects to a loopback address. */ +export async function register(metadata: ServerMetadata): Promise { + if (!metadata.registrationEndpoint) { + throw new Error("This server does not let the CLI register itself"); + } + const response = await fetch(metadata.registrationEndpoint, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + client_name: "MediaLit CLI", + application_type: "native", + // The server accepts any port on a loopback redirect (RFC 8252). + redirect_uris: [`http://127.0.0.1${CALLBACK_PATH}`], + grant_types: ["authorization_code", "refresh_token"], + response_types: ["code"], + token_endpoint_auth_method: "none", + scope: SCOPES, + }), + }); + const body = await readJson(response); + if (!response.ok || !body.client_id) { + throw new Error( + `Could not register the CLI: ${errorMessage(body, response)}`, + ); + } + return body.client_id; +} + +/** + * Runs the authorization code flow with PKCE. The user signs in and picks an + * app in the browser, which then redirects to a server on this machine. + */ +export async function authorize( + metadata: ServerMetadata, + clientId: string, + openUrl: (url: string) => void, +): Promise { + const verifier = base64url(randomBytes(32)); + const challenge = base64url(createHash("sha256").update(verifier).digest()); + const state = base64url(randomBytes(16)); + + const callback = await listenForCallback(state); + const redirectUri = `http://127.0.0.1:${callback.port}${CALLBACK_PATH}`; + const url = new URL(metadata.authorizationEndpoint); + url.search = new URLSearchParams({ + response_type: "code", + client_id: clientId, + redirect_uri: redirectUri, + scope: SCOPES, + state, + code_challenge: challenge, + code_challenge_method: "S256", + resource: metadata.resource, + }).toString(); + + openUrl(url.toString()); + const code = await callback.code; + const tokens = await requestTokens(metadata.tokenEndpoint, { + grant_type: "authorization_code", + code, + redirect_uri: redirectUri, + client_id: clientId, + code_verifier: verifier, + resource: metadata.resource, + }); + if (!tokens.email && metadata.userinfoEndpoint) { + tokens.email = await fetchEmail( + metadata.userinfoEndpoint, + tokens.accessToken, + ); + } + return tokens; +} + +/** Only for showing who is logged in, so failures are ignored. */ +async function fetchEmail( + userinfoEndpoint: string, + accessToken: string, +): Promise { + try { + const response = await fetch(userinfoEndpoint, { + headers: { authorization: `Bearer ${accessToken}` }, + }); + const body = await readJson(response); + return typeof body.email === "string" ? body.email : undefined; + } catch { + return undefined; + } +} + +export function refresh( + tokenEndpoint: string, + input: { clientId: string; refreshToken: string; resource: string }, +): Promise { + return requestTokens(tokenEndpoint, { + grant_type: "refresh_token", + refresh_token: input.refreshToken, + client_id: input.clientId, + resource: input.resource, + }); +} + +export async function revoke( + revocationEndpoint: string, + input: { clientId: string; token: string }, +): Promise { + await fetch(revocationEndpoint, { + method: "POST", + headers: { "content-type": "application/x-www-form-urlencoded" }, + body: new URLSearchParams({ + token: input.token, + token_type_hint: "refresh_token", + client_id: input.clientId, + }), + }); +} + +async function listenForCallback(state: string) { + let settle: { resolve(code: string): void; reject(err: Error): void }; + const code = new Promise((resolve, reject) => { + settle = { resolve, reject }; + }); + + const server = http.createServer((req, res) => { + const url = new URL(req.url || "/", "http://127.0.0.1"); + if (url.pathname !== CALLBACK_PATH) { + res.writeHead(404).end(); + return; + } + const error = url.searchParams.get("error"); + const received = url.searchParams.get("code"); + if (error || !received || url.searchParams.get("state") !== state) { + respond(res, 400, "Login failed. Return to your terminal."); + settle.reject( + new Error( + url.searchParams.get("error_description") || + error || + "The login response was not valid", + ), + ); + return; + } + respond(res, 200, "You're logged in. You can close this tab."); + settle.resolve(received); + }); + await new Promise((resolve) => + server.listen(0, "127.0.0.1", resolve), + ); + + const timeout = setTimeout( + () => settle.reject(new Error("Timed out waiting for the login")), + LOGIN_TIMEOUT_MS, + ); + code.finally(() => { + clearTimeout(timeout); + server.close(); + }).catch(() => undefined); + + return { port: (server.address() as AddressInfo).port, code }; +} + +function respond(res: http.ServerResponse, status: number, message: string) { + res.writeHead(status, { "content-type": "text/html; charset=utf-8" }); + res.end( + `MediaLit CLI` + + `` + + `

${message}

`, + ); +} + +async function requestTokens( + tokenEndpoint: string, + params: Record, +): Promise { + const response = await fetch(tokenEndpoint, { + method: "POST", + headers: { "content-type": "application/x-www-form-urlencoded" }, + body: new URLSearchParams(params), + }); + const body = await readJson(response); + if (!response.ok || !body.access_token) { + throw new Error(errorMessage(body, response)); + } + return { + accessToken: body.access_token, + refreshToken: body.refresh_token, + expiresAt: body.expires_in + ? Date.now() + body.expires_in * 1000 + : undefined, + email: jwtClaim(body.id_token, "email"), + appId: jwtClaim(body.access_token, "app_id"), + }; +} + +/** Reads a claim without verifying the token. Only for display. */ +export function jwtClaim(token: unknown, name: string): string | undefined { + if (typeof token !== "string") return undefined; + const payload = token.split(".")[1]; + if (!payload) return undefined; + try { + const value = JSON.parse(Buffer.from(payload, "base64url").toString())[ + name + ]; + return typeof value === "string" ? value : undefined; + } catch { + return undefined; + } +} + +async function getJson(url: string) { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`${url} responded with ${response.status}`); + } + return response.json(); +} + +async function readJson(response: Response) { + return response.json().catch(() => ({})); +} + +function errorMessage(body: any, response: Response): string { + return ( + body?.error_description || + body?.error || + `the server responded with ${response.status}` + ); +} + +function base64url(bytes: Buffer): string { + return bytes.toString("base64url"); +} diff --git a/packages/cli/src/session.ts b/packages/cli/src/session.ts new file mode 100644 index 00000000..54940572 --- /dev/null +++ b/packages/cli/src/session.ts @@ -0,0 +1,76 @@ +import { MediaLit } from "medialit"; +import { + DEFAULT_ENDPOINT, + loadCredentials, + normalizeEndpoint, + saveCredentials, +} from "./credentials"; +import { refresh } from "./oauth"; + +// Refresh a little early so a token doesn't expire mid-command. +const REFRESH_MARGIN_MS = 60 * 1000; + +export async function resolveEndpoint(flag?: string): Promise { + if (flag) return normalizeEndpoint(flag); + if (process.env.MEDIALIT_ENDPOINT) { + return normalizeEndpoint(process.env.MEDIALIT_ENDPOINT); + } + return (await loadCredentials()).current || DEFAULT_ENDPOINT; +} + +/** + * A client for the given server. `MEDIALIT_API_KEY` wins, for CI; otherwise + * the login saved by `medialit login`, refreshed when it is about to expire. + */ +export async function getClient( + endpointFlag?: string, +): Promise<{ client: MediaLit; endpoint: string }> { + const endpoint = await resolveEndpoint(endpointFlag); + if (process.env.MEDIALIT_API_KEY) { + return { + client: new MediaLit({ + apiKey: process.env.MEDIALIT_API_KEY, + endpoint, + }), + endpoint, + }; + } + + const credentials = await loadCredentials(); + const server = credentials.servers[endpoint]; + if (!server?.accessToken) { + throw new Error( + `Not logged in to ${endpoint}. Run \`medialit login\` first.`, + ); + } + + if (server.expiresAt && server.expiresAt - Date.now() < REFRESH_MARGIN_MS) { + if (!server.refreshToken) { + throw new Error( + "Your login has expired. Run `medialit login` again.", + ); + } + let tokens; + try { + tokens = await refresh(server.tokenEndpoint, { + clientId: server.clientId, + refreshToken: server.refreshToken, + resource: server.resource, + }); + } catch { + throw new Error( + "Your login has expired. Run `medialit login` again.", + ); + } + server.accessToken = tokens.accessToken; + server.refreshToken = tokens.refreshToken || server.refreshToken; + server.expiresAt = tokens.expiresAt; + server.appId = tokens.appId || server.appId; + await saveCredentials(credentials); + } + + return { + client: new MediaLit({ accessToken: server.accessToken, endpoint }), + endpoint, + }; +} diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json new file mode 100644 index 00000000..14b99c9a --- /dev/null +++ b/packages/cli/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "lib": ["ES2022", "DOM"], + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "types": ["node"], + "noEmit": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "__tests__"] +} diff --git a/packages/cli/tsup.config.ts b/packages/cli/tsup.config.ts new file mode 100644 index 00000000..f881dd14 --- /dev/null +++ b/packages/cli/tsup.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + format: ["esm"], + target: "node18", + clean: true, + banner: { js: "#!/usr/bin/env node" }, + outExtension: () => ({ js: ".mjs" }), +}); diff --git a/packages/images/package.json b/packages/images/package.json index a2ab95e1..da3fddcb 100644 --- a/packages/images/package.json +++ b/packages/images/package.json @@ -1,5 +1,6 @@ { "name": "@medialit/images", + "private": true, "version": "0.0.26", "description": "Image processing utilities", "keywords": [ @@ -9,7 +10,7 @@ "author": "Rajat Saxena ", "homepage": "https://github.com/codelitdev/medialit#readme", "license": "MIT", - "main": "dist/index.js", + "main": "./lib/index.ts", "directories": { "lib": "lib", "test": "__tests__" @@ -18,7 +19,9 @@ "dist" ], "publishConfig": { - "access": "public" + "access": "public", + "main": "dist/index.js", + "types": "dist/index.d.ts" }, "repository": { "type": "git", @@ -27,7 +30,7 @@ "scripts": { "test": "echo \"Error: run tests from root\" && exit 1", "build": "tsc", - "prepublishOnly": "pnpm build" + "prepublishOnly": "bun run build" }, "bugs": { "url": "https://github.com/codelitdev/medialit/issues" @@ -36,6 +39,6 @@ "@types/node": "^20.8.7", "typescript": "^5.2.2" }, - "types": "dist/index.d.ts", + "types": "./lib/index.ts", "gitHead": "5f90ae4867c92ea853fe55962ae3fe677ece0f77" -} \ No newline at end of file +} diff --git a/packages/integration-tests/README.md b/packages/integration-tests/README.md new file mode 100644 index 00000000..abdaeafa --- /dev/null +++ b/packages/integration-tests/README.md @@ -0,0 +1,38 @@ +# @medialit/integration-tests + +End-to-end tests and a synthetic check for a [MediaLit](https://medialit.cloud) server, hosted or self-hosted. + +## Synthetic check + +A quick check that MediaLit works end to end. It checks `/ready`, then uploads a tiny public image, seals it, downloads it and deletes it, in about a second. It is safe to run against production every few minutes. + +```bash +MEDIALIT_APIKEY=... MEDIALIT_SERVER=https://medialit.example.com \ + npx @medialit/integration-tests@0.1.0 synthetic +``` + +- Use the API key of an app that exists only for monitoring. The synthetic check never changes settings and only deletes files in its own `__synthetic_` groups, including leftovers from runs that were cut short. +- Set `MEDIALIT_SYNTHETIC_HEARTBEAT_URL` to a heartbeat monitor such as Better Stack or Healthchecks.io. The synthetic check requests the URL after each passing run and `/fail` after a failing one, so you are alerted when MediaLit fails and when the synthetic check stops running. +- It exits with 1 when a step fails, and names the step and the reason. +- Pin the version. The monitoring key is in the synthetic check's environment, so don't run whatever version is newest. + +## Test suites + +The suites cover the REST API, the MCP server and the CLI in depth. They upload files, change media settings and restore them, so run them against a test app with no other activity. + +```bash +MEDIALIT_APIKEY=... MEDIALIT_SERVER=localhost:8000 \ + npx @medialit/integration-tests@0.1.0 all +``` + +| Command | Runs | +| ----------- | -------------------------------------------------------------------- | +| `synthetic` | The synthetic check | +| `all` | The REST, MCP and CLI suites | +| `rest` | The REST API suite | +| `mcp` | The MCP server suite | +| `cli` | The CLI suite. Set `MEDIALIT_CLI` to the `medialit` command to test. | + +In the MediaLit repository, run them with `bun run test:integration`, or `bun --filter @medialit/integration-tests