diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..90409b8 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,49 @@ +name: Deploy Docs + +on: + push: + branches: [main] + paths: + - 'docs/**' + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Install dependencies + run: cd docs && bun install --frozen-lockfile + + - name: Build static site + run: cd docs && bun run build + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs/out + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/README.md b/README.md index de1a2bd..b20b76a 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@

+ DocumentationGetting StartedResourcesSDK Usage • @@ -25,7 +26,7 @@ Strimulator is a drop-in local replacement for the Stripe API. It runs as a sing - **Full control** — Trigger payment failures, advance subscriptions, simulate edge cases from the dashboard - **SDK-compatible** — Point the official `stripe` package at localhost and it just works - **Docker-ready** — Drop it into your docker-compose alongside Postgres, Redis, Firebase emulator, etc. -- **464 tests** — Strict fidelity to Stripe's API shapes, state machines, and error formats +- **496 tests** — Strict fidelity to Stripe's API shapes, state machines, and error formats ## Getting Started @@ -275,8 +276,21 @@ tests/ unit/ # Service-layer tests integration/ # HTTP request/response tests sdk/ # Tests using the official stripe npm package +docs/ # Fumadocs documentation site (Next.js) ``` +## Documentation + +Full documentation is available in the `docs/` directory. To run it locally: + +```bash +cd docs +bun install +bun run dev +``` + +Then open http://localhost:3000 for the docs site with Getting Started guides, API reference, and architecture documentation. + ## Tech Stack - **Runtime:** [Bun](https://bun.sh) @@ -284,7 +298,8 @@ tests/ - **Database:** SQLite via [Drizzle ORM](https://orm.drizzle.team) + bun:sqlite - **Types:** Imported from the [`stripe`](https://www.npmjs.com/package/stripe) npm package - **Dashboard:** [Preact](https://preactjs.com) + [HTM](https://github.com/developit/htm) (loaded from CDN) -- **Testing:** bun:test (464 tests) +- **Testing:** bun:test (496 tests) +- **Documentation:** [Fumadocs](https://fumadocs.dev) + OpenAPI ## License diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..0bae005 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,4 @@ +.next/ +node_modules/ +.source/ +out/ diff --git a/docs/app/docs/[[...slug]]/page.tsx b/docs/app/docs/[[...slug]]/page.tsx new file mode 100644 index 0000000..2dc950b --- /dev/null +++ b/docs/app/docs/[[...slug]]/page.tsx @@ -0,0 +1,42 @@ +import { source } from '@/lib/source'; +import { notFound } from 'next/navigation'; +import { + DocsPage, + DocsBody, + DocsTitle, + DocsDescription, +} from 'fumadocs-ui/page'; +import { getMDXComponents } from '@/components/mdx'; +import type { ComponentType } from 'react'; + +export default async function Page({ + params, +}: { + params: Promise<{ slug?: string[] }>; +}) { + const { slug } = await params; + const page = source.getPage(slug); + + if (!page) notFound(); + + const data = page.data as typeof page.data & { + body: ComponentType<{ components?: Record }>; + toc: { depth: number; url: string; title: string }[]; + }; + + const Mdx = data.body; + + return ( + + {data.title} + {data.description} + + + + + ); +} + +export function generateStaticParams() { + return source.generateParams(); +} diff --git a/docs/app/docs/layout.tsx b/docs/app/docs/layout.tsx new file mode 100644 index 0000000..59876d8 --- /dev/null +++ b/docs/app/docs/layout.tsx @@ -0,0 +1,12 @@ +import { DocsLayout } from 'fumadocs-ui/layouts/docs'; +import { baseOptions } from '@/lib/layout.shared'; +import { source } from '@/lib/source'; +import type { ReactNode } from 'react'; + +export default function Layout({ children }: { children: ReactNode }) { + return ( + + {children} + + ); +} diff --git a/docs/app/global.css b/docs/app/global.css new file mode 100644 index 0000000..1f53f61 --- /dev/null +++ b/docs/app/global.css @@ -0,0 +1,6 @@ +@import 'tailwindcss'; +@import 'fumadocs-ui/css/neutral.css'; +@import 'fumadocs-ui/css/preset.css'; + +@source '../node_modules/fumadocs-ui/dist/**/*.js'; +@source '../node_modules/fumadocs-openapi/dist/**/*.js'; diff --git a/docs/app/layout.tsx b/docs/app/layout.tsx new file mode 100644 index 0000000..6570be0 --- /dev/null +++ b/docs/app/layout.tsx @@ -0,0 +1,13 @@ +import { RootProvider } from 'fumadocs-ui/provider/next'; +import type { ReactNode } from 'react'; +import './global.css'; + +export default function Layout({ children }: { children: ReactNode }) { + return ( + + + {children} + + + ); +} diff --git a/docs/app/page.tsx b/docs/app/page.tsx new file mode 100644 index 0000000..88715e3 --- /dev/null +++ b/docs/app/page.tsx @@ -0,0 +1,5 @@ +import { redirect } from 'next/navigation'; + +export default function Home() { + redirect('/docs'); +} diff --git a/docs/bun.lock b/docs/bun.lock new file mode 100644 index 0000000..9494600 --- /dev/null +++ b/docs/bun.lock @@ -0,0 +1,846 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "strimulator-docs", + "dependencies": { + "fumadocs-core": "^15.8.5", + "fumadocs-mdx": "^12.0.3", + "fumadocs-openapi": "^6.3.0", + "fumadocs-ui": "^15.8.5", + "next": "^15", + "react": "^19", + "react-dom": "^19", + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/react": "^19", + "@types/react-dom": "^19", + "tailwindcss": "^4", + "typescript": "^5", + }, + }, + }, + "packages": { + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], + + "@apidevtools/json-schema-ref-parser": ["@apidevtools/json-schema-ref-parser@11.9.3", "", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0" } }, "sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="], + + "@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=="], + + "@floating-ui/core": ["@floating-ui/core@1.7.5", "", { "dependencies": { "@floating-ui/utils": "^0.2.11" } }, "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.7.6", "", { "dependencies": { "@floating-ui/core": "^1.7.5", "@floating-ui/utils": "^0.2.11" } }, "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ=="], + + "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.8", "", { "dependencies": { "@floating-ui/dom": "^1.7.6" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.11", "", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="], + + "@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.6.2", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA=="], + + "@fumari/json-schema-to-typescript": ["@fumari/json-schema-to-typescript@1.1.3", "", { "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.9.3", "js-yaml": "^4.1.0", "prettier": "^3.5.3" } }, "sha512-KnaZAo5W769nOaxhPqEMTdjHdngugxmPpNS+Yr2U90iVxgmNAWwhSr8Nx3l+CUehJKNFzJi2C7clQXOfuPJegA=="], + + "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], + + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], + + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], + + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], + + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], + + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], + + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], + + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], + + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], + + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], + + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], + + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], + + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], + + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], + + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], + + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], + + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], + + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], + + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], + + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], + + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], + + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], + + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], + + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], + + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + + "@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/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/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@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=="], + + "@jsdevtools/ono": ["@jsdevtools/ono@7.1.3", "", {}, "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg=="], + + "@mdx-js/mdx": ["@mdx-js/mdx@3.1.1", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "acorn": "^8.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "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.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ=="], + + "@next/env": ["@next/env@15.5.15", "", {}, "sha512-vcmyu5/MyFzN7CdqRHO3uHO44p/QPCZkuTUXroeUmhNP8bL5PHFEhik22JUazt+CDDoD6EpBYRCaS2pISL+/hg=="], + + "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.5.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-6PvFO2Tzt10GFK2Ro9tAVEtacMqRmTarYMFKAnV2vYMdwWc73xzmDQyAV7SwEdMhzmiRoo7+m88DuiXlJlGeaw=="], + + "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.5.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-G+YNV+z6FDZTp/+IdGyIMFqalBTaQSnvAA+X/hrt+eaTRFSznRMz9K7rTmzvM6tDmKegNtyzgufZW0HwVzEqaQ=="], + + "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.5.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-eVkrMcVIBqGfXB+QUC7jjZ94Z6uX/dNStbQFabewAnk13Uy18Igd1YZ/GtPRzdhtm7QwC0e6o7zOQecul4iC1w=="], + + "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.5.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-RwSHKMQ7InLy5GfkY2/n5PcFycKA08qI1VST78n09nN36nUPqCvGSMiLXlfUmzmpQpF6XeBYP2KRWHi0UW3uNg=="], + + "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.5.15", "", { "os": "linux", "cpu": "x64" }, "sha512-nplqvY86LakS+eeiuWsNWvfmK8pFcOEW7ZtVRt4QH70lL+0x6LG/m1OpJ/tvrbwjmR8HH9/fH2jzW1GlL03TIg=="], + + "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.5.15", "", { "os": "linux", "cpu": "x64" }, "sha512-eAgl9NKQ84/sww0v81DQINl/vL2IBxD7sMybd0cWRw6wqgouVI53brVRBrggqBRP/NWeIAE1dm5cbKYoiMlqDQ=="], + + "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.5.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-GJVZC86lzSquh0MtvZT+L7G8+jMnJcldloOjA8Kf3wXvBrvb6OGe2MzPuALxFshSm/IpwUtD2mIoof39ymf52A=="], + + "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.5.15", "", { "os": "win32", "cpu": "x64" }, "sha512-nFucjVdwlFqxh/JG3hWSJ4p8+YJV7Ii8aPDuBQULB6DzUF4UNZETXLfEUk+oI2zEznWWULPt7MeuTE6xtK1HSA=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "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.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@orama/orama": ["@orama/orama@3.1.18", "", {}, "sha512-a61ljmRVVyG5MC/698C8/FfFDw5a8LOIvyOLW5fztgUXqUpc1jOfQzOitSCbge657OgXXThmY3Tk8fpiDb4UcA=="], + + "@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-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.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-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.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-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.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-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-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-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-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.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-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA=="], + + "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@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.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-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-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-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-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-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.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-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-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.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-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/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="], + + "@scalar/openapi-parser": ["@scalar/openapi-parser@0.10.9", "", { "dependencies": { "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "ajv-formats": "^3.0.1", "jsonpointer": "^5.0.1", "leven": "^4.0.0", "yaml": "^2.4.5" } }, "sha512-wsKZtL4B3Fmbv24B1zzeWdsEr6F60fLmToOFLA1b9QGQ6TAtIvLgKQWQ65eh5Vfx93nQb/iekamfErqHRHtEhA=="], + + "@shikijs/core": ["@shikijs/core@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA=="], + + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA=="], + + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g=="], + + "@shikijs/langs": ["@shikijs/langs@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg=="], + + "@shikijs/rehype": ["@shikijs/rehype@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@types/hast": "^3.0.4", "hast-util-to-string": "^3.0.1", "shiki": "3.23.0", "unified": "^11.0.5", "unist-util-visit": "^5.1.0" } }, "sha512-GepKJxXHbXFfAkiZZZ+4V7x71Lw3s0ALYmydUxJRdvpKjSx9FOMSaunv6WRLFBXR6qjYerUq1YZQno+2gLEPwA=="], + + "@shikijs/themes": ["@shikijs/themes@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA=="], + + "@shikijs/transformers": ["@shikijs/transformers@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/types": "3.23.0" } }, "sha512-F9msZVxdF+krQNSdQ4V+Ja5QemeAoTQ2jxt7nJCwhDsdF1JWS3KxIQXA3lQbyKwS3J61oHRUSv4jYWv3CkaKTQ=="], + + "@shikijs/types": ["@shikijs/types@3.23.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ=="], + + "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + + "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.2.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.2" } }, "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.2", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.2", "@tailwindcss/oxide-darwin-arm64": "4.2.2", "@tailwindcss/oxide-darwin-x64": "4.2.2", "@tailwindcss/oxide-freebsd-x64": "4.2.2", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", "@tailwindcss/oxide-linux-x64-musl": "4.2.2", "@tailwindcss/oxide-wasm32-wasi": "4.2.2", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" } }, "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.2", "", { "os": "android", "cpu": "arm64" }, "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2", "", { "os": "linux", "cpu": "arm" }, "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.2", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.2", "", { "os": "win32", "cpu": "x64" }, "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA=="], + + "@tailwindcss/postcss": ["@tailwindcss/postcss@4.2.2", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.2.2", "@tailwindcss/oxide": "4.2.2", "postcss": "^8.5.6", "tailwindcss": "4.2.2" } }, "sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ=="], + + "@types/debug": ["@types/debug@4.1.13", "", { "dependencies": { "@types/ms": "*" } }, "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="], + + "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], + + "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], + + "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], + + "@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], + + "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], + + "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + + "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + + "ajv-draft-04": ["ajv-draft-04@1.0.0", "", { "peerDependencies": { "ajv": "^8.5.0" }, "optionalPeers": ["ajv"] }, "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw=="], + + "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], + + "astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="], + + "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001787", "", {}, "sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg=="], + + "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + + "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=="], + + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + + "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], + + "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="], + + "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], + + "compute-scroll-into-view": ["compute-scroll-into-view@3.1.1", "", {}, "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw=="], + + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], + + "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], + + "enhanced-resolve": ["enhanced-resolve@5.20.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA=="], + + "esast-util-from-estree": ["esast-util-from-estree@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.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.0", "acorn": "^8.0.0", "esast-util-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "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=="], + + "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + + "estree-util-attach-comments": ["estree-util-attach-comments@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw=="], + + "estree-util-build-jsx": ["estree-util-build-jsx@3.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-walker": "^3.0.0" } }, "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.0", "devlop": "^1.0.0" } }, "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ=="], + + "estree-util-to-js": ["estree-util-to-js@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "astring": "^1.8.0", "source-map": "^0.7.0" } }, "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg=="], + + "estree-util-value-to-estree": ["estree-util-value-to-estree@3.5.0", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-aMV56R27Gv3QmfmF1MY12GWkGzzeAezAX+UplqHVASfjc9wNzI/X6hC0S9oxq61WT4aQesLGslWP9tKk6ghRZQ=="], + + "estree-util-visit": ["estree-util-visit@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/unist": "^3.0.0" } }, "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww=="], + + "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], + + "fast-xml-builder": ["fast-xml-builder@1.1.4", "", { "dependencies": { "path-expression-matcher": "^1.1.3" } }, "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg=="], + + "fast-xml-parser": ["fast-xml-parser@5.5.11", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.4.0", "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-QL0eb0YbSTVWF6tTf1+LEMSgtCEjBYPpnAjoLC8SscESlAjXEIRJ7cHtLG0pLeDFaZLa4VKZLArtA/60ZS7vyA=="], + + "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "foreach": ["foreach@2.0.6", "", {}, "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg=="], + + "fumadocs-core": ["fumadocs-core@15.8.5", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.6.2", "@orama/orama": "^3.1.14", "@shikijs/rehype": "^3.13.0", "@shikijs/transformers": "^3.13.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", "negotiator": "^1.0.0", "npm-to-yarn": "^3.0.1", "path-to-regexp": "^8.3.0", "react-remove-scroll": "^2.7.1", "remark": "^15.0.1", "remark-gfm": "^4.0.1", "remark-rehype": "^11.1.2", "scroll-into-view-if-needed": "^3.1.0", "shiki": "^3.13.0", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "@mixedbread/sdk": "^0.19.0", "@oramacloud/client": "1.x.x || 2.x.x", "@tanstack/react-router": "1.x.x", "@types/react": "*", "algoliasearch": "5.x.x", "lucide-react": "*", "next": "14.x.x || 15.x.x", "react": "18.x.x || 19.x.x", "react-dom": "18.x.x || 19.x.x", "react-router": "7.x.x", "waku": "^0.26.0" }, "optionalPeers": ["@mixedbread/sdk", "@oramacloud/client", "@tanstack/react-router", "@types/react", "algoliasearch", "lucide-react", "next", "react", "react-dom", "react-router", "waku"] }, "sha512-hyJtKGuB2J/5y7tDfI1EnGMKlNbSXM5N5cpwvgCY0DcBJwFMDG/GpSpaVRzh3aWy67pAYDZFIwdtbKXBa/q5bg=="], + + "fumadocs-mdx": ["fumadocs-mdx@12.0.3", "", { "dependencies": { "@mdx-js/mdx": "^3.1.1", "@standard-schema/spec": "^1.0.0", "chokidar": "^4.0.3", "esbuild": "^0.25.10", "estree-util-value-to-estree": "^3.4.0", "js-yaml": "^4.1.0", "lru-cache": "^11.2.2", "mdast-util-to-markdown": "^2.1.2", "picocolors": "^1.1.1", "remark-mdx": "^3.1.1", "remark-parse": "^11.0.0", "tinyexec": "^1.0.1", "tinyglobby": "^0.2.15", "unified": "^11.0.5", "unist-util-visit": "^5.0.0", "zod": "^4.1.11" }, "peerDependencies": { "@fumadocs/mdx-remote": "^1.4.0", "fumadocs-core": "^14.0.0 || ^15.0.0", "next": "^15.3.0", "react": "*", "vite": "6.x.x || 7.x.x" }, "optionalPeers": ["@fumadocs/mdx-remote", "next", "react", "vite"], "bin": { "fumadocs-mdx": "dist/bin.js" } }, "sha512-OYqbHSmzkejG+iUMlZJJOitaVbCgBdo/REc/9Sq1WaZ1vq6bH9PCFU0cKJlRdHbQSGRfVg5EJJy5uKy5+iNFGQ=="], + + "fumadocs-openapi": ["fumadocs-openapi@6.3.0", "", { "dependencies": { "@fumari/json-schema-to-typescript": "^1.1.2", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-select": "^2.1.6", "@radix-ui/react-slot": "^1.1.2", "@scalar/openapi-parser": "0.10.9", "ajv-draft-04": "^1.0.0", "class-variance-authority": "^0.7.1", "fast-glob": "^3.3.3", "fumadocs-core": "15.0.15", "fumadocs-ui": "15.0.15", "github-slugger": "^2.0.0", "hast-util-to-jsx-runtime": "^2.3.5", "js-yaml": "^4.1.0", "lucide-react": "^0.477.0", "next-themes": "^0.4.4", "openapi-sampler": "^1.6.1", "react-hook-form": "^7.54.2", "remark": "^15.0.1", "remark-rehype": "^11.1.1", "shiki": "^3.1.0", "xml-js": "^1.6.11" }, "peerDependencies": { "@scalar/api-client-react": "*", "next": "14.x.x || 15.x.x", "react": "18.x.x || 19.x.x", "react-dom": "18.x.x || 19.x.x" }, "optionalPeers": ["@scalar/api-client-react"] }, "sha512-PEE8txaKkBDjacedRCRD4ekkmfOYx5mWjIc+g+Ys84/zkm5ALaRxAernTMsbAck8GjOOZmA/5MFG/nL/hRvchA=="], + + "fumadocs-ui": ["fumadocs-ui@15.8.5", "", { "dependencies": { "@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.3", "@radix-ui/react-tabs": "^1.1.13", "class-variance-authority": "^0.7.1", "fumadocs-core": "15.8.5", "lodash.merge": "^4.6.2", "next-themes": "^0.4.6", "postcss-selector-parser": "^7.1.0", "react-medium-image-zoom": "^5.4.0", "scroll-into-view-if-needed": "^3.1.0", "tailwind-merge": "^3.3.1" }, "peerDependencies": { "@types/react": "*", "next": "14.x.x || 15.x.x", "react": "18.x.x || 19.x.x", "react-dom": "18.x.x || 19.x.x", "tailwindcss": "^3.4.14 || ^4.0.0" }, "optionalPeers": ["@types/react", "next", "tailwindcss"] }, "sha512-9pyB+9rOOsrFnmmZ9xREp/OgVhyaSq2ocEpqTNbeQ7tlJ6JWbdFWfW0C9lRXprQEB6DJWUDtDxqKS5QXLH0EGA=="], + + "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], + + "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], + + "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "hast-util-to-estree": ["hast-util-to-estree@3.1.3", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.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.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w=="], + + "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "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.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="], + + "hast-util-to-string": ["hast-util-to-string@3.0.1", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A=="], + + "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], + + "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], + + "image-size": ["image-size@2.0.2", "", { "bin": { "image-size": "bin/image-size.js" } }, "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w=="], + + "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="], + + "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.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="], + + "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "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-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], + + "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + + "json-pointer": ["json-pointer@0.6.2", "", { "dependencies": { "foreach": "^2.0.4" } }, "sha512-vLWcKbOaXlO+jvRy4qNd+TI1QUPZzfJj1tpJ3vAXDych5XJf93ftpUKe5pKCrzyIIwgBJcOcCVRUfqQP25afBw=="], + + "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "jsonpointer": ["jsonpointer@5.0.1", "", {}, "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="], + + "leven": ["leven@4.1.0", "", {}, "sha512-KZ9W9nWDT7rF7Dazg8xyLHGLrmpgq2nVNFUckhqdW3szVP6YhCpp/RAnpmVExA9JvrMynjwSLVrEj3AepHR6ew=="], + + "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], + + "lru-cache": ["lru-cache@11.3.3", "", {}, "sha512-JvNw9Y81y33E+BEYPr0U7omo+U9AySnsMsEiXgwT6yqd31VQWTLNQqmT4ou5eqPFUrTfIDFta2wKhB1hyohtAQ=="], + + "lucide-react": ["lucide-react@0.477.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-yCf7aYxerFZAbd8jHJxjwe1j7jEMPptjnaOqdYeirFnEy85cNR3/L+o0I875CYFYya+eEVzZSbNuRk8BZPDpVw=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "markdown-extensions": ["markdown-extensions@2.0.0", "", {}, "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q=="], + + "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], + + "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="], + + "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q=="], + + "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.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.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="], + + "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="], + + "mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="], + + "mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg=="], + + "mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "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.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="], + + "mdast-util-mdx": ["mdast-util-mdx@3.0.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w=="], + + "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="], + + "mdast-util-mdx-jsx": ["mdast-util-mdx-jsx@3.2.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" } }, "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q=="], + + "mdast-util-mdxjs-esm": ["mdast-util-mdxjs-esm@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg=="], + + "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "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.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="], + + "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="], + + "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], + + "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="], + + "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], + + "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="], + + "micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="], + + "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="], + + "micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="], + + "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="], + + "micromark-extension-mdx-expression": ["micromark-extension-mdx-expression@3.0.1", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q=="], + + "micromark-extension-mdx-jsx": ["micromark-extension-mdx-jsx@3.0.2", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ=="], + + "micromark-extension-mdx-md": ["micromark-extension-mdx-md@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ=="], + + "micromark-extension-mdxjs": ["micromark-extension-mdxjs@3.0.0", "", { "dependencies": { "acorn": "^8.0.0", "acorn-jsx": "^5.0.0", "micromark-extension-mdx-expression": "^3.0.0", "micromark-extension-mdx-jsx": "^3.0.0", "micromark-extension-mdx-md": "^2.0.0", "micromark-extension-mdxjs-esm": "^3.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ=="], + + "micromark-extension-mdxjs-esm": ["micromark-extension-mdxjs-esm@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-position-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A=="], + + "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="], + + "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="], + + "micromark-factory-mdx-expression": ["micromark-factory-mdx-expression@2.0.3", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-position-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ=="], + + "micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="], + + "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="], + + "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="], + + "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], + + "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="], + + "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="], + + "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="], + + "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="], + + "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "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.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "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.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="], + + "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="], + + "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], + + "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "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=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + + "next": ["next@15.5.15", "", { "dependencies": { "@next/env": "15.5.15", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.5.15", "@next/swc-darwin-x64": "15.5.15", "@next/swc-linux-arm64-gnu": "15.5.15", "@next/swc-linux-arm64-musl": "15.5.15", "@next/swc-linux-x64-gnu": "15.5.15", "@next/swc-linux-x64-musl": "15.5.15", "@next/swc-win32-arm64-msvc": "15.5.15", "@next/swc-win32-x64-msvc": "15.5.15", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-VSqCrJwtLVGwAVE0Sb/yikrQfkwkZW9p+lL/J4+xe+G3ZA+QnWPqgcfH1tDUEuk9y+pthzzVFp4L/U8JerMfMQ=="], + + "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=="], + + "npm-to-yarn": ["npm-to-yarn@3.0.1", "", {}, "sha512-tt6PvKu4WyzPwWUzy/hvPFqn+uwXO0K1ZHka8az3NnrhWJDmSqI8ncWq0fkL0k/lmmi5tAC11FXwXuh0rFbt1A=="], + + "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.7", "fast-xml-parser": "^5.5.1", "json-pointer": "0.6.2" } }, "sha512-OKytvqB5XIaTgA9xtw8W8UTar+uymW2xPVpFN0NihMtuHPdPTGxBEhGnfFnJW5g/gOSIvkP+H0Xh3XhVI9/n7g=="], + + "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], + + "path-expression-matcher": ["path-expression-matcher@1.5.0", "", {}, "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ=="], + + "path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], + + "postcss": ["postcss@8.5.9", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw=="], + + "postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], + + "prettier": ["prettier@3.8.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-8c3mgTe0ASwWAJK+78dpviD+A8EqhndQPUBpNUIPt6+xWlIigCwfN01lWr9MAede4uqXGTEKeQWTvzb3vjia0Q=="], + + "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "react": ["react@19.2.5", "", {}, "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA=="], + + "react-dom": ["react-dom@19.2.5", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.5" } }, "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag=="], + + "react-hook-form": ["react-hook-form@7.72.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-RhwBoy2ygeVZje+C+bwJ8g0NjTdBmDlJvAUHTxRjTmSUKPYsKfMphkS2sgEMotsY03bP358yEYlnUeZy//D9Ig=="], + + "react-medium-image-zoom": ["react-medium-image-zoom@5.4.3", "", { "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-cDIwdn35fRUPsGnnj/cG6Pacll+z+Mfv6EWU2wDO5ngbZjg5uLRb2ZhEnh92ufbXCJDFvXHekb8G3+oKqUcv5g=="], + + "react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "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.2", "tslib": "^2.0.0" }, "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.0", "tslib": "^2.0.0" }, "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=="], + + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + + "recma-build-jsx": ["recma-build-jsx@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-build-jsx": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew=="], + + "recma-jsx": ["recma-jsx@1.0.1", "", { "dependencies": { "acorn-jsx": "^5.0.0", "estree-util-to-js": "^2.0.0", "recma-parse": "^1.0.0", "recma-stringify": "^1.0.0", "unified": "^11.0.0" }, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w=="], + + "recma-parse": ["recma-parse@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "esast-util-from-js": "^2.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ=="], + + "recma-stringify": ["recma-stringify@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-to-js": "^2.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g=="], + + "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="], + + "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=="], + + "rehype-recma": ["rehype-recma@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "hast-util-to-estree": "^3.0.0" } }, "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw=="], + + "remark": ["remark@15.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A=="], + + "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], + + "remark-mdx": ["remark-mdx@3.1.1", "", { "dependencies": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" } }, "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg=="], + + "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], + + "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="], + + "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], + + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="], + + "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.0.2" } }, "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ=="], + + "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], + + "shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="], + + "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + + "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], + + "strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="], + + "style-to-js": ["style-to-js@1.1.21", "", { "dependencies": { "style-to-object": "1.0.14" } }, "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ=="], + + "style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="], + + "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], + + "tailwind-merge": ["tailwind-merge@3.5.0", "", {}, "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A=="], + + "tailwindcss": ["tailwindcss@4.2.2", "", {}, "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q=="], + + "tapable": ["tapable@2.3.2", "", {}, "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA=="], + + "tinyexec": ["tinyexec@1.1.1", "", {}, "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg=="], + + "tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], + + "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], + + "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], + + "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], + + "unist-util-position-from-estree": ["unist-util-position-from-estree@2.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ=="], + + "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], + + "unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="], + + "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], + + "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "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.0.0" }, "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=="], + + "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], + + "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], + + "xml-js": ["xml-js@1.6.11", "", { "dependencies": { "sax": "^1.2.4" }, "bin": { "xml-js": "./bin/cli.js" } }, "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g=="], + + "yaml": ["yaml@2.8.3", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg=="], + + "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + + "@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-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-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-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-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=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.3", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "fumadocs-openapi/fumadocs-core": ["fumadocs-core@15.0.15", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.6.0", "@orama/orama": "^3.1.1", "@shikijs/rehype": "^3.1.0", "@shikijs/transformers": "^3.1.0", "github-slugger": "^2.0.0", "hast-util-to-estree": "^3.1.2", "hast-util-to-jsx-runtime": "^2.3.5", "image-size": "^2.0.0", "negotiator": "^1.0.0", "react-remove-scroll": "^2.6.3", "remark": "^15.0.0", "remark-gfm": "^4.0.1", "scroll-into-view-if-needed": "^3.1.0", "shiki": "^3.1.0", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "@oramacloud/client": "1.x.x || 2.x.x", "algoliasearch": "4.24.0", "next": "14.x.x || 15.x.x", "react": "18.x.x || 19.x.x", "react-dom": "18.x.x || 19.x.x" }, "optionalPeers": ["@oramacloud/client", "algoliasearch", "next", "react", "react-dom"] }, "sha512-roWAf6voyhpLpbdWalCBW4x4h2LVEJZ/J1sGG+3cR27QLn/5Vv/AEjPB0mFRcoH/fkEVU1wilxvgfDOpRXWDcA=="], + + "fumadocs-openapi/fumadocs-ui": ["fumadocs-ui@15.0.15", "", { "dependencies": { "@radix-ui/react-accordion": "^1.2.3", "@radix-ui/react-collapsible": "^1.1.3", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-direction": "^1.1.0", "@radix-ui/react-navigation-menu": "^1.2.5", "@radix-ui/react-popover": "^1.1.6", "@radix-ui/react-scroll-area": "^1.2.3", "@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-tabs": "^1.1.3", "class-variance-authority": "^0.7.1", "lodash.merge": "^4.6.2", "lucide-react": "^0.477.0", "next-themes": "^0.4.4", "postcss-selector-parser": "^7.1.0", "react-medium-image-zoom": "^5.2.14", "tailwind-merge": "^3.0.2" }, "peerDependencies": { "fumadocs-core": "15.0.15", "next": "14.x.x || 15.x.x", "react": "18.x.x || 19.x.x", "react-dom": "18.x.x || 19.x.x", "tailwindcss": "^3.4.14 || ^4.0.0" }, "optionalPeers": ["tailwindcss"] }, "sha512-D9x+GJlTgFe+te6h22Xp3OHDYGzEp/iWm86HiQkJQLhT5jwZ5/2htF/ehzBwOm+bizGvH6WIe3ZYfJ3dfer0/A=="], + + "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + + "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], + + "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], + } +} diff --git a/docs/components/api-page.client.tsx b/docs/components/api-page.client.tsx new file mode 100644 index 0000000..5df1b8d --- /dev/null +++ b/docs/components/api-page.client.tsx @@ -0,0 +1,2 @@ +'use client'; +export { Samples, Sample } from 'fumadocs-openapi/ui/client'; diff --git a/docs/components/api-page.tsx b/docs/components/api-page.tsx new file mode 100644 index 0000000..d3d7cd4 --- /dev/null +++ b/docs/components/api-page.tsx @@ -0,0 +1,3 @@ +import { openapi } from '@/lib/openapi'; + +export const { APIPage } = openapi; diff --git a/docs/components/mdx.tsx b/docs/components/mdx.tsx new file mode 100644 index 0000000..b3ef2fb --- /dev/null +++ b/docs/components/mdx.tsx @@ -0,0 +1,10 @@ +import defaultMdxComponents from 'fumadocs-ui/mdx'; +import { APIPage } from '@/components/api-page'; +import type { MDXComponents } from 'mdx/types'; + +export function getMDXComponents(): MDXComponents { + return { + ...defaultMdxComponents, + APIPage, + }; +} diff --git a/docs/content/docs/api/advance-test-clock.mdx b/docs/content/docs/api/advance-test-clock.mdx new file mode 100644 index 0000000..62f008c --- /dev/null +++ b/docs/content/docs/api/advance-test-clock.mdx @@ -0,0 +1,26 @@ +--- +title: Advance a test clock +description: >- + Advances a test clock's frozen time forward. This triggers billing cycle + processing for any linked subscriptions, including rolling periods, creating + invoices, and processing payments. +full: true +_openapi: + method: POST + route: /v1/test_helpers/test_clocks/{id}/advance + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Advances a test clock's frozen time forward. This triggers billing + cycle processing for any linked subscriptions, including rolling + periods, creating invoices, and processing payments. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Advances a test clock's frozen time forward. This triggers billing cycle processing for any linked subscriptions, including rolling periods, creating invoices, and processing payments. + + \ No newline at end of file diff --git a/docs/content/docs/api/attach-payment-method.mdx b/docs/content/docs/api/attach-payment-method.mdx new file mode 100644 index 0000000..b4375df --- /dev/null +++ b/docs/content/docs/api/attach-payment-method.mdx @@ -0,0 +1,20 @@ +--- +title: Attach a payment method to a customer +description: Attaches a payment method object to a customer. +full: true +_openapi: + method: POST + route: /v1/payment_methods/{id}/attach + toc: [] + structuredData: + headings: [] + contents: + - content: Attaches a payment method object to a customer. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Attaches a payment method object to a customer. + + \ No newline at end of file diff --git a/docs/content/docs/api/cancel-payment-intent.mdx b/docs/content/docs/api/cancel-payment-intent.mdx new file mode 100644 index 0000000..18a09ad --- /dev/null +++ b/docs/content/docs/api/cancel-payment-intent.mdx @@ -0,0 +1,24 @@ +--- +title: Cancel a payment intent +description: >- + Cancels a PaymentIntent. You cannot cancel a PaymentIntent with a status of + `succeeded`. +full: true +_openapi: + method: POST + route: /v1/payment_intents/{id}/cancel + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Cancels a PaymentIntent. You cannot cancel a PaymentIntent with a + status of `succeeded`. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Cancels a PaymentIntent. You cannot cancel a PaymentIntent with a status of `succeeded`. + + \ No newline at end of file diff --git a/docs/content/docs/api/cancel-setup-intent.mdx b/docs/content/docs/api/cancel-setup-intent.mdx new file mode 100644 index 0000000..20e4469 --- /dev/null +++ b/docs/content/docs/api/cancel-setup-intent.mdx @@ -0,0 +1,24 @@ +--- +title: Cancel a setup intent +description: >- + Cancels a SetupIntent. You cannot cancel a SetupIntent that has already + succeeded. +full: true +_openapi: + method: POST + route: /v1/setup_intents/{id}/cancel + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Cancels a SetupIntent. You cannot cancel a SetupIntent that has + already succeeded. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Cancels a SetupIntent. You cannot cancel a SetupIntent that has already succeeded. + + \ No newline at end of file diff --git a/docs/content/docs/api/cancel-subscription.mdx b/docs/content/docs/api/cancel-subscription.mdx new file mode 100644 index 0000000..73fb373 --- /dev/null +++ b/docs/content/docs/api/cancel-subscription.mdx @@ -0,0 +1,24 @@ +--- +title: Cancel a subscription +description: >- + Cancels a customer's subscription immediately. The subscription status + transitions to `canceled`. +full: true +_openapi: + method: DELETE + route: /v1/subscriptions/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Cancels a customer's subscription immediately. The subscription status + transitions to `canceled`. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Cancels a customer's subscription immediately. The subscription status transitions to `canceled`. + + \ No newline at end of file diff --git a/docs/content/docs/api/capture-payment-intent.mdx b/docs/content/docs/api/capture-payment-intent.mdx new file mode 100644 index 0000000..75d5ed4 --- /dev/null +++ b/docs/content/docs/api/capture-payment-intent.mdx @@ -0,0 +1,24 @@ +--- +title: Capture a payment intent +description: >- + Captures a PaymentIntent that has a status of `requires_capture`. This is used + when `capture_method=manual`. +full: true +_openapi: + method: POST + route: /v1/payment_intents/{id}/capture + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Captures a PaymentIntent that has a status of `requires_capture`. This + is used when `capture_method=manual`. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Captures a PaymentIntent that has a status of `requires_capture`. This is used when `capture_method=manual`. + + \ No newline at end of file diff --git a/docs/content/docs/api/confirm-payment-intent.mdx b/docs/content/docs/api/confirm-payment-intent.mdx new file mode 100644 index 0000000..5b420b4 --- /dev/null +++ b/docs/content/docs/api/confirm-payment-intent.mdx @@ -0,0 +1,26 @@ +--- +title: Confirm a payment intent +description: >- + Confirms a PaymentIntent, transitioning it to `succeeded`, `requires_capture`, + or `requires_action` (for 3DS). Payment methods with last4 `3220` + (`tok_threeDSecureRequired`) trigger 3DS simulation. +full: true +_openapi: + method: POST + route: /v1/payment_intents/{id}/confirm + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Confirms a PaymentIntent, transitioning it to `succeeded`, + `requires_capture`, or `requires_action` (for 3DS). Payment methods + with last4 `3220` (`tok_threeDSecureRequired`) trigger 3DS simulation. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Confirms a PaymentIntent, transitioning it to `succeeded`, `requires_capture`, or `requires_action` (for 3DS). Payment methods with last4 `3220` (`tok_threeDSecureRequired`) trigger 3DS simulation. + + \ No newline at end of file diff --git a/docs/content/docs/api/confirm-setup-intent.mdx b/docs/content/docs/api/confirm-setup-intent.mdx new file mode 100644 index 0000000..339ef5c --- /dev/null +++ b/docs/content/docs/api/confirm-setup-intent.mdx @@ -0,0 +1,25 @@ +--- +title: Confirm a setup intent +description: >- + Confirms a SetupIntent, transitioning it to `succeeded`. If a `payment_method` + is provided, it attaches the payment method to the customer. +full: true +_openapi: + method: POST + route: /v1/setup_intents/{id}/confirm + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Confirms a SetupIntent, transitioning it to `succeeded`. If a + `payment_method` is provided, it attaches the payment method to the + customer. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Confirms a SetupIntent, transitioning it to `succeeded`. If a `payment_method` is provided, it attaches the payment method to the customer. + + \ No newline at end of file diff --git a/docs/content/docs/api/create-customer.mdx b/docs/content/docs/api/create-customer.mdx new file mode 100644 index 0000000..c347ce6 --- /dev/null +++ b/docs/content/docs/api/create-customer.mdx @@ -0,0 +1,20 @@ +--- +title: Create a customer +description: Creates a new customer object. +full: true +_openapi: + method: POST + route: /v1/customers + toc: [] + structuredData: + headings: [] + contents: + - content: Creates a new customer object. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Creates a new customer object. + + \ No newline at end of file diff --git a/docs/content/docs/api/create-invoice.mdx b/docs/content/docs/api/create-invoice.mdx new file mode 100644 index 0000000..de64f00 --- /dev/null +++ b/docs/content/docs/api/create-invoice.mdx @@ -0,0 +1,24 @@ +--- +title: Create an invoice +description: >- + Creates a new invoice for a customer. Invoices are created as drafts and must + be finalized before they can be paid. +full: true +_openapi: + method: POST + route: /v1/invoices + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Creates a new invoice for a customer. Invoices are created as drafts + and must be finalized before they can be paid. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Creates a new invoice for a customer. Invoices are created as drafts and must be finalized before they can be paid. + + \ No newline at end of file diff --git a/docs/content/docs/api/create-payment-intent.mdx b/docs/content/docs/api/create-payment-intent.mdx new file mode 100644 index 0000000..b023ad8 --- /dev/null +++ b/docs/content/docs/api/create-payment-intent.mdx @@ -0,0 +1,24 @@ +--- +title: Create a payment intent +description: >- + Creates a PaymentIntent object. Optionally set `confirm=true` with a + `payment_method` to create and confirm in one step. +full: true +_openapi: + method: POST + route: /v1/payment_intents + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Creates a PaymentIntent object. Optionally set `confirm=true` with a + `payment_method` to create and confirm in one step. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Creates a PaymentIntent object. Optionally set `confirm=true` with a `payment_method` to create and confirm in one step. + + \ No newline at end of file diff --git a/docs/content/docs/api/create-payment-method.mdx b/docs/content/docs/api/create-payment-method.mdx new file mode 100644 index 0000000..2962751 --- /dev/null +++ b/docs/content/docs/api/create-payment-method.mdx @@ -0,0 +1,27 @@ +--- +title: Create a payment method +description: >- + Creates a payment method object. Strimulator supports `card` type with magic + test tokens like `tok_visa`, `tok_mastercard`, `tok_amex`, `tok_visa_debit`, + `tok_threeDSecureRequired`, and `tok_threeDSecureOptional`. +full: true +_openapi: + method: POST + route: /v1/payment_methods + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Creates a payment method object. Strimulator supports `card` type with + magic test tokens like `tok_visa`, `tok_mastercard`, `tok_amex`, + `tok_visa_debit`, `tok_threeDSecureRequired`, and + `tok_threeDSecureOptional`. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Creates a payment method object. Strimulator supports `card` type with magic test tokens like `tok_visa`, `tok_mastercard`, `tok_amex`, `tok_visa_debit`, `tok_threeDSecureRequired`, and `tok_threeDSecureOptional`. + + \ No newline at end of file diff --git a/docs/content/docs/api/create-price.mdx b/docs/content/docs/api/create-price.mdx new file mode 100644 index 0000000..6beb6b1 --- /dev/null +++ b/docs/content/docs/api/create-price.mdx @@ -0,0 +1,24 @@ +--- +title: Create a price +description: >- + Creates a new price for an existing product. The price can be one-time or + recurring. +full: true +_openapi: + method: POST + route: /v1/prices + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Creates a new price for an existing product. The price can be one-time + or recurring. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Creates a new price for an existing product. The price can be one-time or recurring. + + \ No newline at end of file diff --git a/docs/content/docs/api/create-product.mdx b/docs/content/docs/api/create-product.mdx new file mode 100644 index 0000000..96defc7 --- /dev/null +++ b/docs/content/docs/api/create-product.mdx @@ -0,0 +1,20 @@ +--- +title: Create a product +description: Creates a new product object. +full: true +_openapi: + method: POST + route: /v1/products + toc: [] + structuredData: + headings: [] + contents: + - content: Creates a new product object. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Creates a new product object. + + \ No newline at end of file diff --git a/docs/content/docs/api/create-refund.mdx b/docs/content/docs/api/create-refund.mdx new file mode 100644 index 0000000..79bd34b --- /dev/null +++ b/docs/content/docs/api/create-refund.mdx @@ -0,0 +1,24 @@ +--- +title: Create a refund +description: >- + Creates a refund for a charge or payment intent. You must provide either + `charge` or `payment_intent`. +full: true +_openapi: + method: POST + route: /v1/refunds + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Creates a refund for a charge or payment intent. You must provide + either `charge` or `payment_intent`. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Creates a refund for a charge or payment intent. You must provide either `charge` or `payment_intent`. + + \ No newline at end of file diff --git a/docs/content/docs/api/create-setup-intent.mdx b/docs/content/docs/api/create-setup-intent.mdx new file mode 100644 index 0000000..fde21f2 --- /dev/null +++ b/docs/content/docs/api/create-setup-intent.mdx @@ -0,0 +1,24 @@ +--- +title: Create a setup intent +description: >- + Creates a SetupIntent object for setting up a customer's payment method for + future payments. +full: true +_openapi: + method: POST + route: /v1/setup_intents + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Creates a SetupIntent object for setting up a customer's payment + method for future payments. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Creates a SetupIntent object for setting up a customer's payment method for future payments. + + \ No newline at end of file diff --git a/docs/content/docs/api/create-subscription.mdx b/docs/content/docs/api/create-subscription.mdx new file mode 100644 index 0000000..6d62e1b --- /dev/null +++ b/docs/content/docs/api/create-subscription.mdx @@ -0,0 +1,24 @@ +--- +title: Create a subscription +description: >- + Creates a new subscription on an existing customer. Each subscription must + have at least one item with a recurring price. +full: true +_openapi: + method: POST + route: /v1/subscriptions + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Creates a new subscription on an existing customer. Each subscription + must have at least one item with a recurring price. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Creates a new subscription on an existing customer. Each subscription must have at least one item with a recurring price. + + \ No newline at end of file diff --git a/docs/content/docs/api/create-test-clock.mdx b/docs/content/docs/api/create-test-clock.mdx new file mode 100644 index 0000000..8dfe842 --- /dev/null +++ b/docs/content/docs/api/create-test-clock.mdx @@ -0,0 +1,25 @@ +--- +title: Create a test clock +description: >- + Creates a new test clock with the specified frozen time. Use test clocks to + simulate the passage of time for subscription billing and trial expirations. +full: true +_openapi: + method: POST + route: /v1/test_helpers/test_clocks + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Creates a new test clock with the specified frozen time. Use test + clocks to simulate the passage of time for subscription billing and + trial expirations. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Creates a new test clock with the specified frozen time. Use test clocks to simulate the passage of time for subscription billing and trial expirations. + + \ No newline at end of file diff --git a/docs/content/docs/api/create-webhook-endpoint.mdx b/docs/content/docs/api/create-webhook-endpoint.mdx new file mode 100644 index 0000000..ec50a52 --- /dev/null +++ b/docs/content/docs/api/create-webhook-endpoint.mdx @@ -0,0 +1,25 @@ +--- +title: Create a webhook endpoint +description: >- + Creates a webhook endpoint that listens to specific events. Strimulator will + POST event payloads to the configured URL with HMAC-SHA256 signatures. +full: true +_openapi: + method: POST + route: /v1/webhook_endpoints + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Creates a webhook endpoint that listens to specific events. + Strimulator will POST event payloads to the configured URL with + HMAC-SHA256 signatures. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Creates a webhook endpoint that listens to specific events. Strimulator will POST event payloads to the configured URL with HMAC-SHA256 signatures. + + \ No newline at end of file diff --git a/docs/content/docs/api/delete-customer.mdx b/docs/content/docs/api/delete-customer.mdx new file mode 100644 index 0000000..5bcfc73 --- /dev/null +++ b/docs/content/docs/api/delete-customer.mdx @@ -0,0 +1,20 @@ +--- +title: Delete a customer +description: Permanently deletes a customer. It cannot be undone. +full: true +_openapi: + method: DELETE + route: /v1/customers/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: Permanently deletes a customer. It cannot be undone. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Permanently deletes a customer. It cannot be undone. + + \ No newline at end of file diff --git a/docs/content/docs/api/delete-product.mdx b/docs/content/docs/api/delete-product.mdx new file mode 100644 index 0000000..00ff698 --- /dev/null +++ b/docs/content/docs/api/delete-product.mdx @@ -0,0 +1,20 @@ +--- +title: Delete a product +description: Permanently deletes a product. +full: true +_openapi: + method: DELETE + route: /v1/products/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: Permanently deletes a product. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Permanently deletes a product. + + \ No newline at end of file diff --git a/docs/content/docs/api/delete-test-clock.mdx b/docs/content/docs/api/delete-test-clock.mdx new file mode 100644 index 0000000..90f9741 --- /dev/null +++ b/docs/content/docs/api/delete-test-clock.mdx @@ -0,0 +1,20 @@ +--- +title: Delete a test clock +description: Deletes a test clock and all associated resources. +full: true +_openapi: + method: DELETE + route: /v1/test_helpers/test_clocks/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: Deletes a test clock and all associated resources. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Deletes a test clock and all associated resources. + + \ No newline at end of file diff --git a/docs/content/docs/api/delete-webhook-endpoint.mdx b/docs/content/docs/api/delete-webhook-endpoint.mdx new file mode 100644 index 0000000..a05a84d --- /dev/null +++ b/docs/content/docs/api/delete-webhook-endpoint.mdx @@ -0,0 +1,22 @@ +--- +title: Delete a webhook endpoint +description: Deletes a webhook endpoint. Strimulator will stop sending events to this URL. +full: true +_openapi: + method: DELETE + route: /v1/webhook_endpoints/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Deletes a webhook endpoint. Strimulator will stop sending events to + this URL. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Deletes a webhook endpoint. Strimulator will stop sending events to this URL. + + \ No newline at end of file diff --git a/docs/content/docs/api/detach-payment-method.mdx b/docs/content/docs/api/detach-payment-method.mdx new file mode 100644 index 0000000..e635983 --- /dev/null +++ b/docs/content/docs/api/detach-payment-method.mdx @@ -0,0 +1,25 @@ +--- +title: Detach a payment method from its customer +description: >- + Detaches a payment method object from the customer it is attached to. The + payment method still exists but can no longer be used for payments. +full: true +_openapi: + method: POST + route: /v1/payment_methods/{id}/detach + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Detaches a payment method object from the customer it is attached to. + The payment method still exists but can no longer be used for + payments. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Detaches a payment method object from the customer it is attached to. The payment method still exists but can no longer be used for payments. + + \ No newline at end of file diff --git a/docs/content/docs/api/finalize-invoice.mdx b/docs/content/docs/api/finalize-invoice.mdx new file mode 100644 index 0000000..3c1750e --- /dev/null +++ b/docs/content/docs/api/finalize-invoice.mdx @@ -0,0 +1,24 @@ +--- +title: Finalize an invoice +description: >- + Finalizes a draft invoice, transitioning it to `open` status and assigning an + invoice number. Once finalized, the invoice can be paid. +full: true +_openapi: + method: POST + route: /v1/invoices/{id}/finalize + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Finalizes a draft invoice, transitioning it to `open` status and + assigning an invoice number. Once finalized, the invoice can be paid. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Finalizes a draft invoice, transitioning it to `open` status and assigning an invoice number. Once finalized, the invoice can be paid. + + \ No newline at end of file diff --git a/docs/content/docs/api/list-charges.mdx b/docs/content/docs/api/list-charges.mdx new file mode 100644 index 0000000..b52a3f7 --- /dev/null +++ b/docs/content/docs/api/list-charges.mdx @@ -0,0 +1,22 @@ +--- +title: List all charges +description: Returns a paginated list of charges. Optionally filter by payment intent. +full: true +_openapi: + method: GET + route: /v1/charges + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Returns a paginated list of charges. Optionally filter by payment + intent. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Returns a paginated list of charges. Optionally filter by payment intent. + + \ No newline at end of file diff --git a/docs/content/docs/api/list-customers.mdx b/docs/content/docs/api/list-customers.mdx new file mode 100644 index 0000000..5ec9c2c --- /dev/null +++ b/docs/content/docs/api/list-customers.mdx @@ -0,0 +1,24 @@ +--- +title: List all customers +description: >- + Returns a paginated list of customers, sorted by creation date (most recent + first). +full: true +_openapi: + method: GET + route: /v1/customers + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Returns a paginated list of customers, sorted by creation date (most + recent first). +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Returns a paginated list of customers, sorted by creation date (most recent first). + + \ No newline at end of file diff --git a/docs/content/docs/api/list-events.mdx b/docs/content/docs/api/list-events.mdx new file mode 100644 index 0000000..2194e8d --- /dev/null +++ b/docs/content/docs/api/list-events.mdx @@ -0,0 +1,24 @@ +--- +title: List all events +description: >- + Returns a paginated list of events, sorted by creation date (most recent + first). Optionally filter by event type. +full: true +_openapi: + method: GET + route: /v1/events + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Returns a paginated list of events, sorted by creation date (most + recent first). Optionally filter by event type. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Returns a paginated list of events, sorted by creation date (most recent first). Optionally filter by event type. + + \ No newline at end of file diff --git a/docs/content/docs/api/list-invoices.mdx b/docs/content/docs/api/list-invoices.mdx new file mode 100644 index 0000000..774fed7 --- /dev/null +++ b/docs/content/docs/api/list-invoices.mdx @@ -0,0 +1,24 @@ +--- +title: List all invoices +description: >- + Returns a paginated list of invoices. Optionally filter by customer or + subscription. +full: true +_openapi: + method: GET + route: /v1/invoices + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Returns a paginated list of invoices. Optionally filter by customer or + subscription. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Returns a paginated list of invoices. Optionally filter by customer or subscription. + + \ No newline at end of file diff --git a/docs/content/docs/api/list-payment-intents.mdx b/docs/content/docs/api/list-payment-intents.mdx new file mode 100644 index 0000000..b63ebb3 --- /dev/null +++ b/docs/content/docs/api/list-payment-intents.mdx @@ -0,0 +1,22 @@ +--- +title: List payment intents +description: Returns a paginated list of PaymentIntents. Optionally filter by customer. +full: true +_openapi: + method: GET + route: /v1/payment_intents + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Returns a paginated list of PaymentIntents. Optionally filter by + customer. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Returns a paginated list of PaymentIntents. Optionally filter by customer. + + \ No newline at end of file diff --git a/docs/content/docs/api/list-payment-methods.mdx b/docs/content/docs/api/list-payment-methods.mdx new file mode 100644 index 0000000..0e8df93 --- /dev/null +++ b/docs/content/docs/api/list-payment-methods.mdx @@ -0,0 +1,20 @@ +--- +title: List payment methods +description: Returns a list of payment methods for a given customer. +full: true +_openapi: + method: GET + route: /v1/payment_methods + toc: [] + structuredData: + headings: [] + contents: + - content: Returns a list of payment methods for a given customer. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Returns a list of payment methods for a given customer. + + \ No newline at end of file diff --git a/docs/content/docs/api/list-prices.mdx b/docs/content/docs/api/list-prices.mdx new file mode 100644 index 0000000..ecdb42b --- /dev/null +++ b/docs/content/docs/api/list-prices.mdx @@ -0,0 +1,20 @@ +--- +title: List all prices +description: Returns a paginated list of prices. Optionally filter by product. +full: true +_openapi: + method: GET + route: /v1/prices + toc: [] + structuredData: + headings: [] + contents: + - content: Returns a paginated list of prices. Optionally filter by product. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Returns a paginated list of prices. Optionally filter by product. + + \ No newline at end of file diff --git a/docs/content/docs/api/list-products.mdx b/docs/content/docs/api/list-products.mdx new file mode 100644 index 0000000..66cc044 --- /dev/null +++ b/docs/content/docs/api/list-products.mdx @@ -0,0 +1,20 @@ +--- +title: List all products +description: Returns a paginated list of products. +full: true +_openapi: + method: GET + route: /v1/products + toc: [] + structuredData: + headings: [] + contents: + - content: Returns a paginated list of products. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Returns a paginated list of products. + + \ No newline at end of file diff --git a/docs/content/docs/api/list-refunds.mdx b/docs/content/docs/api/list-refunds.mdx new file mode 100644 index 0000000..b88199d --- /dev/null +++ b/docs/content/docs/api/list-refunds.mdx @@ -0,0 +1,24 @@ +--- +title: List all refunds +description: >- + Returns a paginated list of refunds. Optionally filter by charge or payment + intent. +full: true +_openapi: + method: GET + route: /v1/refunds + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Returns a paginated list of refunds. Optionally filter by charge or + payment intent. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Returns a paginated list of refunds. Optionally filter by charge or payment intent. + + \ No newline at end of file diff --git a/docs/content/docs/api/list-setup-intents.mdx b/docs/content/docs/api/list-setup-intents.mdx new file mode 100644 index 0000000..41cf2c6 --- /dev/null +++ b/docs/content/docs/api/list-setup-intents.mdx @@ -0,0 +1,20 @@ +--- +title: List setup intents +description: Returns a paginated list of SetupIntents. +full: true +_openapi: + method: GET + route: /v1/setup_intents + toc: [] + structuredData: + headings: [] + contents: + - content: Returns a paginated list of SetupIntents. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Returns a paginated list of SetupIntents. + + \ No newline at end of file diff --git a/docs/content/docs/api/list-subscriptions.mdx b/docs/content/docs/api/list-subscriptions.mdx new file mode 100644 index 0000000..9d955a5 --- /dev/null +++ b/docs/content/docs/api/list-subscriptions.mdx @@ -0,0 +1,22 @@ +--- +title: List subscriptions +description: Returns a paginated list of subscriptions. Optionally filter by customer. +full: true +_openapi: + method: GET + route: /v1/subscriptions + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Returns a paginated list of subscriptions. Optionally filter by + customer. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Returns a paginated list of subscriptions. Optionally filter by customer. + + \ No newline at end of file diff --git a/docs/content/docs/api/list-test-clocks.mdx b/docs/content/docs/api/list-test-clocks.mdx new file mode 100644 index 0000000..b65497a --- /dev/null +++ b/docs/content/docs/api/list-test-clocks.mdx @@ -0,0 +1,20 @@ +--- +title: List test clocks +description: Returns a paginated list of test clocks. +full: true +_openapi: + method: GET + route: /v1/test_helpers/test_clocks + toc: [] + structuredData: + headings: [] + contents: + - content: Returns a paginated list of test clocks. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Returns a paginated list of test clocks. + + \ No newline at end of file diff --git a/docs/content/docs/api/list-webhook-endpoints.mdx b/docs/content/docs/api/list-webhook-endpoints.mdx new file mode 100644 index 0000000..bfabd3e --- /dev/null +++ b/docs/content/docs/api/list-webhook-endpoints.mdx @@ -0,0 +1,20 @@ +--- +title: List webhook endpoints +description: Returns a paginated list of webhook endpoints. +full: true +_openapi: + method: GET + route: /v1/webhook_endpoints + toc: [] + structuredData: + headings: [] + contents: + - content: Returns a paginated list of webhook endpoints. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Returns a paginated list of webhook endpoints. + + \ No newline at end of file diff --git a/docs/content/docs/api/meta.json b/docs/content/docs/api/meta.json new file mode 100644 index 0000000..7db4fe4 --- /dev/null +++ b/docs/content/docs/api/meta.json @@ -0,0 +1,3 @@ +{ + "title": "API Reference" +} diff --git a/docs/content/docs/api/pay-invoice.mdx b/docs/content/docs/api/pay-invoice.mdx new file mode 100644 index 0000000..2cc9903 --- /dev/null +++ b/docs/content/docs/api/pay-invoice.mdx @@ -0,0 +1,20 @@ +--- +title: Pay an invoice +description: Pays an open invoice, transitioning it to `paid` status. +full: true +_openapi: + method: POST + route: /v1/invoices/{id}/pay + toc: [] + structuredData: + headings: [] + contents: + - content: Pays an open invoice, transitioning it to `paid` status. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Pays an open invoice, transitioning it to `paid` status. + + \ No newline at end of file diff --git a/docs/content/docs/api/retrieve-charge.mdx b/docs/content/docs/api/retrieve-charge.mdx new file mode 100644 index 0000000..3da8cbd --- /dev/null +++ b/docs/content/docs/api/retrieve-charge.mdx @@ -0,0 +1,24 @@ +--- +title: Retrieve a charge +description: >- + Retrieves the details of a charge. Supports expansion of `customer` and + `payment_intent`. +full: true +_openapi: + method: GET + route: /v1/charges/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Retrieves the details of a charge. Supports expansion of `customer` + and `payment_intent`. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Retrieves the details of a charge. Supports expansion of `customer` and `payment_intent`. + + \ No newline at end of file diff --git a/docs/content/docs/api/retrieve-customer.mdx b/docs/content/docs/api/retrieve-customer.mdx new file mode 100644 index 0000000..3b295a5 --- /dev/null +++ b/docs/content/docs/api/retrieve-customer.mdx @@ -0,0 +1,20 @@ +--- +title: Retrieve a customer +description: Retrieves a customer object by its ID. +full: true +_openapi: + method: GET + route: /v1/customers/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: Retrieves a customer object by its ID. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Retrieves a customer object by its ID. + + \ No newline at end of file diff --git a/docs/content/docs/api/retrieve-event.mdx b/docs/content/docs/api/retrieve-event.mdx new file mode 100644 index 0000000..9defa8c --- /dev/null +++ b/docs/content/docs/api/retrieve-event.mdx @@ -0,0 +1,20 @@ +--- +title: Retrieve an event +description: Retrieves the details of an event. +full: true +_openapi: + method: GET + route: /v1/events/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: Retrieves the details of an event. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Retrieves the details of an event. + + \ No newline at end of file diff --git a/docs/content/docs/api/retrieve-invoice.mdx b/docs/content/docs/api/retrieve-invoice.mdx new file mode 100644 index 0000000..6047178 --- /dev/null +++ b/docs/content/docs/api/retrieve-invoice.mdx @@ -0,0 +1,24 @@ +--- +title: Retrieve an invoice +description: >- + Retrieves the invoice with the given ID. Supports expansion of `customer`, + `subscription`, and `payment_intent`. +full: true +_openapi: + method: GET + route: /v1/invoices/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Retrieves the invoice with the given ID. Supports expansion of + `customer`, `subscription`, and `payment_intent`. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Retrieves the invoice with the given ID. Supports expansion of `customer`, `subscription`, and `payment_intent`. + + \ No newline at end of file diff --git a/docs/content/docs/api/retrieve-payment-intent.mdx b/docs/content/docs/api/retrieve-payment-intent.mdx new file mode 100644 index 0000000..fba4f2d --- /dev/null +++ b/docs/content/docs/api/retrieve-payment-intent.mdx @@ -0,0 +1,24 @@ +--- +title: Retrieve a payment intent +description: >- + Retrieves the details of a PaymentIntent. Supports expansion of `customer`, + `payment_method`, and `latest_charge`. +full: true +_openapi: + method: GET + route: /v1/payment_intents/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Retrieves the details of a PaymentIntent. Supports expansion of + `customer`, `payment_method`, and `latest_charge`. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Retrieves the details of a PaymentIntent. Supports expansion of `customer`, `payment_method`, and `latest_charge`. + + \ No newline at end of file diff --git a/docs/content/docs/api/retrieve-payment-method.mdx b/docs/content/docs/api/retrieve-payment-method.mdx new file mode 100644 index 0000000..a5d36d9 --- /dev/null +++ b/docs/content/docs/api/retrieve-payment-method.mdx @@ -0,0 +1,20 @@ +--- +title: Retrieve a payment method +description: Retrieves a payment method object. +full: true +_openapi: + method: GET + route: /v1/payment_methods/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: Retrieves a payment method object. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Retrieves a payment method object. + + \ No newline at end of file diff --git a/docs/content/docs/api/retrieve-price.mdx b/docs/content/docs/api/retrieve-price.mdx new file mode 100644 index 0000000..2ba418f --- /dev/null +++ b/docs/content/docs/api/retrieve-price.mdx @@ -0,0 +1,20 @@ +--- +title: Retrieve a price +description: Retrieves the details of an existing price. +full: true +_openapi: + method: GET + route: /v1/prices/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: Retrieves the details of an existing price. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Retrieves the details of an existing price. + + \ No newline at end of file diff --git a/docs/content/docs/api/retrieve-product.mdx b/docs/content/docs/api/retrieve-product.mdx new file mode 100644 index 0000000..116f506 --- /dev/null +++ b/docs/content/docs/api/retrieve-product.mdx @@ -0,0 +1,20 @@ +--- +title: Retrieve a product +description: Retrieves the details of an existing product. +full: true +_openapi: + method: GET + route: /v1/products/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: Retrieves the details of an existing product. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Retrieves the details of an existing product. + + \ No newline at end of file diff --git a/docs/content/docs/api/retrieve-refund.mdx b/docs/content/docs/api/retrieve-refund.mdx new file mode 100644 index 0000000..ea88c47 --- /dev/null +++ b/docs/content/docs/api/retrieve-refund.mdx @@ -0,0 +1,20 @@ +--- +title: Retrieve a refund +description: Retrieves the details of a refund. +full: true +_openapi: + method: GET + route: /v1/refunds/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: Retrieves the details of a refund. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Retrieves the details of a refund. + + \ No newline at end of file diff --git a/docs/content/docs/api/retrieve-setup-intent.mdx b/docs/content/docs/api/retrieve-setup-intent.mdx new file mode 100644 index 0000000..2c41131 --- /dev/null +++ b/docs/content/docs/api/retrieve-setup-intent.mdx @@ -0,0 +1,20 @@ +--- +title: Retrieve a setup intent +description: Retrieves the details of a SetupIntent. +full: true +_openapi: + method: GET + route: /v1/setup_intents/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: Retrieves the details of a SetupIntent. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Retrieves the details of a SetupIntent. + + \ No newline at end of file diff --git a/docs/content/docs/api/retrieve-subscription.mdx b/docs/content/docs/api/retrieve-subscription.mdx new file mode 100644 index 0000000..c8ba1f6 --- /dev/null +++ b/docs/content/docs/api/retrieve-subscription.mdx @@ -0,0 +1,25 @@ +--- +title: Retrieve a subscription +description: >- + Retrieves the subscription with the given ID. Supports expansion of `customer` + and `latest_invoice` (including nested `latest_invoice.payment_intent`). +full: true +_openapi: + method: GET + route: /v1/subscriptions/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Retrieves the subscription with the given ID. Supports expansion of + `customer` and `latest_invoice` (including nested + `latest_invoice.payment_intent`). +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Retrieves the subscription with the given ID. Supports expansion of `customer` and `latest_invoice` (including nested `latest_invoice.payment_intent`). + + \ No newline at end of file diff --git a/docs/content/docs/api/retrieve-test-clock.mdx b/docs/content/docs/api/retrieve-test-clock.mdx new file mode 100644 index 0000000..902793a --- /dev/null +++ b/docs/content/docs/api/retrieve-test-clock.mdx @@ -0,0 +1,20 @@ +--- +title: Retrieve a test clock +description: Retrieves a test clock. +full: true +_openapi: + method: GET + route: /v1/test_helpers/test_clocks/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: Retrieves a test clock. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Retrieves a test clock. + + \ No newline at end of file diff --git a/docs/content/docs/api/retrieve-webhook-endpoint.mdx b/docs/content/docs/api/retrieve-webhook-endpoint.mdx new file mode 100644 index 0000000..cec6596 --- /dev/null +++ b/docs/content/docs/api/retrieve-webhook-endpoint.mdx @@ -0,0 +1,20 @@ +--- +title: Retrieve a webhook endpoint +description: Retrieves the webhook endpoint with the given ID. +full: true +_openapi: + method: GET + route: /v1/webhook_endpoints/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: Retrieves the webhook endpoint with the given ID. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Retrieves the webhook endpoint with the given ID. + + \ No newline at end of file diff --git a/docs/content/docs/api/search-customers.mdx b/docs/content/docs/api/search-customers.mdx new file mode 100644 index 0000000..01b0b96 --- /dev/null +++ b/docs/content/docs/api/search-customers.mdx @@ -0,0 +1,24 @@ +--- +title: Search customers +description: >- + Search for customers using Stripe's search query language. Supports filtering + by `email`, `name`, `metadata`, and other fields. +full: true +_openapi: + method: GET + route: /v1/customers/search + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Search for customers using Stripe's search query language. Supports + filtering by `email`, `name`, `metadata`, and other fields. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Search for customers using Stripe's search query language. Supports filtering by `email`, `name`, `metadata`, and other fields. + + \ No newline at end of file diff --git a/docs/content/docs/api/search-invoices.mdx b/docs/content/docs/api/search-invoices.mdx new file mode 100644 index 0000000..d1d84c5 --- /dev/null +++ b/docs/content/docs/api/search-invoices.mdx @@ -0,0 +1,20 @@ +--- +title: Search invoices +description: Search for invoices using Stripe's search query language. +full: true +_openapi: + method: GET + route: /v1/invoices/search + toc: [] + structuredData: + headings: [] + contents: + - content: Search for invoices using Stripe's search query language. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Search for invoices using Stripe's search query language. + + \ No newline at end of file diff --git a/docs/content/docs/api/search-payment-intents.mdx b/docs/content/docs/api/search-payment-intents.mdx new file mode 100644 index 0000000..5609341 --- /dev/null +++ b/docs/content/docs/api/search-payment-intents.mdx @@ -0,0 +1,20 @@ +--- +title: Search payment intents +description: Search for PaymentIntents using Stripe's search query language. +full: true +_openapi: + method: GET + route: /v1/payment_intents/search + toc: [] + structuredData: + headings: [] + contents: + - content: Search for PaymentIntents using Stripe's search query language. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Search for PaymentIntents using Stripe's search query language. + + \ No newline at end of file diff --git a/docs/content/docs/api/search-subscriptions.mdx b/docs/content/docs/api/search-subscriptions.mdx new file mode 100644 index 0000000..79e5cba --- /dev/null +++ b/docs/content/docs/api/search-subscriptions.mdx @@ -0,0 +1,20 @@ +--- +title: Search subscriptions +description: Search for subscriptions using Stripe's search query language. +full: true +_openapi: + method: GET + route: /v1/subscriptions/search + toc: [] + structuredData: + headings: [] + contents: + - content: Search for subscriptions using Stripe's search query language. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Search for subscriptions using Stripe's search query language. + + \ No newline at end of file diff --git a/docs/content/docs/api/update-customer.mdx b/docs/content/docs/api/update-customer.mdx new file mode 100644 index 0000000..8583b0c --- /dev/null +++ b/docs/content/docs/api/update-customer.mdx @@ -0,0 +1,24 @@ +--- +title: Update a customer +description: >- + Updates the specified customer by setting the values of the parameters passed. + Any parameters not provided will be left unchanged. +full: true +_openapi: + method: POST + route: /v1/customers/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Updates the specified customer by setting the values of the parameters + passed. Any parameters not provided will be left unchanged. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Updates the specified customer by setting the values of the parameters passed. Any parameters not provided will be left unchanged. + + \ No newline at end of file diff --git a/docs/content/docs/api/update-payment-intent.mdx b/docs/content/docs/api/update-payment-intent.mdx new file mode 100644 index 0000000..3d9b315 --- /dev/null +++ b/docs/content/docs/api/update-payment-intent.mdx @@ -0,0 +1,24 @@ +--- +title: Update a payment intent +description: >- + Updates properties on a PaymentIntent without confirming. Returns the current + PaymentIntent. +full: true +_openapi: + method: POST + route: /v1/payment_intents/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Updates properties on a PaymentIntent without confirming. Returns the + current PaymentIntent. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Updates properties on a PaymentIntent without confirming. Returns the current PaymentIntent. + + \ No newline at end of file diff --git a/docs/content/docs/api/update-price.mdx b/docs/content/docs/api/update-price.mdx new file mode 100644 index 0000000..32fba9d --- /dev/null +++ b/docs/content/docs/api/update-price.mdx @@ -0,0 +1,24 @@ +--- +title: Update a price +description: >- + Updates the specified price. Only mutable fields like `active`, `nickname`, + and `metadata` can be changed. +full: true +_openapi: + method: POST + route: /v1/prices/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Updates the specified price. Only mutable fields like `active`, + `nickname`, and `metadata` can be changed. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Updates the specified price. Only mutable fields like `active`, `nickname`, and `metadata` can be changed. + + \ No newline at end of file diff --git a/docs/content/docs/api/update-product.mdx b/docs/content/docs/api/update-product.mdx new file mode 100644 index 0000000..b6e2d63 --- /dev/null +++ b/docs/content/docs/api/update-product.mdx @@ -0,0 +1,22 @@ +--- +title: Update a product +description: Updates the specified product by setting the values of the parameters passed. +full: true +_openapi: + method: POST + route: /v1/products/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Updates the specified product by setting the values of the parameters + passed. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Updates the specified product by setting the values of the parameters passed. + + \ No newline at end of file diff --git a/docs/content/docs/api/update-subscription.mdx b/docs/content/docs/api/update-subscription.mdx new file mode 100644 index 0000000..3bf810c --- /dev/null +++ b/docs/content/docs/api/update-subscription.mdx @@ -0,0 +1,24 @@ +--- +title: Update a subscription +description: >- + Updates an existing subscription. You can change the plan, quantity, trial + end, and cancellation settings. +full: true +_openapi: + method: POST + route: /v1/subscriptions/{id} + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Updates an existing subscription. You can change the plan, quantity, + trial end, and cancellation settings. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Updates an existing subscription. You can change the plan, quantity, trial end, and cancellation settings. + + \ No newline at end of file diff --git a/docs/content/docs/api/void-invoice.mdx b/docs/content/docs/api/void-invoice.mdx new file mode 100644 index 0000000..406cb0c --- /dev/null +++ b/docs/content/docs/api/void-invoice.mdx @@ -0,0 +1,24 @@ +--- +title: Void an invoice +description: >- + Voids an open invoice, transitioning it to `void` status. A voided invoice can + no longer be paid. +full: true +_openapi: + method: POST + route: /v1/invoices/{id}/void + toc: [] + structuredData: + headings: [] + contents: + - content: >- + Voids an open invoice, transitioning it to `void` status. A voided + invoice can no longer be paid. +--- + +{/* This file was generated by Fumadocs. +Do not edit this file directly. Any changes should be made by running the generation command again. */} + +Voids an open invoice, transitioning it to `void` status. A voided invoice can no longer be paid. + + \ No newline at end of file diff --git a/docs/content/docs/architecture/contributing.mdx b/docs/content/docs/architecture/contributing.mdx new file mode 100644 index 0000000..e72ae0c --- /dev/null +++ b/docs/content/docs/architecture/contributing.mdx @@ -0,0 +1,45 @@ +--- +title: Contributing +description: Development setup and guidelines for contributors +--- + +## Development Setup + +```bash +git clone https://github.com/codeforge-tech/strimulator.git +cd strimulator +bun install +bun run dev # Start with watch mode +``` + +## Running Tests + +```bash +bun test # Run all tests (unit + integration + SDK) +bun test tests/unit/ # Run unit tests only +bun test tests/sdk/ # Run SDK tests only +bun test tests/integration/customers.test.ts # Single file +bun x tsc --noEmit # Type check +``` + +## Adding a New Resource + +1. **Schema:** Create `src/db/schema/.ts` with the Drizzle table definition +2. **Service:** Create `src/services/.ts` with a class that takes `StrimulatorDB` +3. **Routes:** Create `src/routes/.ts` as an Elysia plugin factory +4. **Wire up:** Register routes in `src/app.ts` +5. **Tests:** Add unit tests in `tests/unit/` and SDK tests in `tests/sdk/` + +## Conventions + +- **IDs:** Generated via `generateId(type)` with crypto.randomBytes. Each type has a prefix (`cus_`, `pi_`, `sub_`, etc.) defined in `src/lib/id-generator.ts`. +- **Body format:** Always `application/x-www-form-urlencoded` with bracket notation. Use `parseStripeBody()`. +- **Soft deletes:** Use a `deleted` integer flag (0/1), never hard-delete. +- **No conventional commits:** Write clear descriptions, don't prefix with `feat:`, `fix:`, etc. + +## Database Migrations + +```bash +bun run db:generate # Generate migration from schema changes +bun run db:migrate # Apply migrations +``` diff --git a/docs/content/docs/architecture/meta.json b/docs/content/docs/architecture/meta.json new file mode 100644 index 0000000..d99279a --- /dev/null +++ b/docs/content/docs/architecture/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Architecture", + "pages": ["overview", "request-lifecycle", "services", "contributing"] +} diff --git a/docs/content/docs/architecture/overview.mdx b/docs/content/docs/architecture/overview.mdx new file mode 100644 index 0000000..8efeabe --- /dev/null +++ b/docs/content/docs/architecture/overview.mdx @@ -0,0 +1,41 @@ +--- +title: Architecture Overview +description: How Strimulator is built +--- + +Strimulator emulates Stripe's REST API over HTTP using Elysia (Bun's web framework) + SQLite via Drizzle ORM. It returns real `Stripe.*` types from the official `stripe` npm package. + +## Tech Stack + +| Component | Technology | +|-----------|-----------| +| Runtime | [Bun](https://bun.sh) | +| Framework | [ElysiaJS](https://elysiajs.com) | +| Database | SQLite via [Drizzle ORM](https://orm.drizzle.team) + bun:sqlite | +| Types | Imported from [`stripe`](https://www.npmjs.com/package/stripe) npm package | +| Dashboard | [Preact](https://preactjs.com) + [HTM](https://github.com/developit/htm) | +| Testing | bun:test | + +## Project Structure + +``` +src/ + routes/ # ElysiaJS route plugins (one per Stripe resource) + services/ # Business logic and state machines + db/schema/ # Drizzle ORM table definitions + middleware/ # Auth, idempotency, form parsing, request logging + dashboard/ # Debug dashboard (API + Preact SPA) + lib/ # Shared utilities (IDs, pagination, expand, search) + errors/ # Stripe-compatible error factory +tests/ + unit/ # Service-layer tests + integration/ # HTTP request/response tests + sdk/ # Tests using the official stripe npm package +``` + +## Key Design Decisions + +- **Stripe type fidelity:** Services return `Stripe.*` types directly from the official package, ensuring response shapes always match real Stripe. +- **Synchronous DB:** bun:sqlite is synchronous, so services are synchronous. Routes are async only for body parsing and expansion. +- **In-memory by default:** Using `:memory:` SQLite for ephemeral storage makes tests fast and isolated. File-backed persistence is opt-in. +- **Single process:** No external dependencies — one binary, one port, everything included. diff --git a/docs/content/docs/architecture/request-lifecycle.mdx b/docs/content/docs/architecture/request-lifecycle.mdx new file mode 100644 index 0000000..936a008 --- /dev/null +++ b/docs/content/docs/architecture/request-lifecycle.mdx @@ -0,0 +1,65 @@ +--- +title: Request Lifecycle +description: How an HTTP request flows through Strimulator +--- + +Every API request passes through a middleware chain before reaching the route handler: + +``` +HTTP request + → apiKeyAuth (validates Bearer sk_test_*) + → idempotencyMiddleware (caches POST responses by Idempotency-Key header) + → requestLogger (emits to globalBus for dashboard SSE) + → route handler + → parseStripeBody() (decodes x-www-form-urlencoded with bracket notation) + → service method (DB read/write, state validation) + → eventService.emit() (triggers webhook delivery) + → response +``` + +## Middleware + +### API Key Auth + +Validates the `Authorization: Bearer sk_test_*` header. Any key starting with `sk_test_` is accepted — Strimulator doesn't verify specific keys. + +Source: `src/middleware/api-key-auth.ts` + +### Idempotency + +Caches POST responses keyed by the `Idempotency-Key` header. Replay requests return the cached response without re-executing the handler. + +Source: `src/middleware/idempotency.ts` + +### Request Logger + +Emits every request to the global event bus for the dashboard's live activity feed via SSE. + +Source: `src/middleware/request-logger.ts` + +### Form Parser + +Decodes `application/x-www-form-urlencoded` bodies with Stripe's bracket notation (`metadata[key]=value`, `items[0][price]=...`). + +Source: `src/middleware/form-parser.ts` + +## Route → Service → Response + +Routes are Elysia plugin factories that: + +1. Parse the form-encoded body with `parseStripeBody()` +2. Call the appropriate service method +3. Emit events via `eventService.emit()` (triggers webhook delivery) +4. Return the Stripe-typed response + +## Expansion + +Routes that support `?expand[]=field` use `applyExpand()` with a config mapping field names to resolver functions. Supports nested expansion via dot notation (`expand[]=latest_invoice.payment_intent`). + +Source: `src/lib/expand.ts` + +## Pagination + +All list endpoints use cursor-based pagination via `created` timestamp and `starting_after` parameter. Returns `{ object: "list", data: [...], has_more: boolean }`. + +Source: `src/lib/pagination.ts` diff --git a/docs/content/docs/architecture/services.mdx b/docs/content/docs/architecture/services.mdx new file mode 100644 index 0000000..defe781 --- /dev/null +++ b/docs/content/docs/architecture/services.mdx @@ -0,0 +1,75 @@ +--- +title: Services +description: Business logic layer and state machines +--- + +Services are classes in `src/services/` that encapsulate all business logic. Each service takes `StrimulatorDB` in the constructor and returns `Stripe.*` types. + +## Pattern + +```typescript +class CustomerService { + constructor(private db: StrimulatorDB) {} + + create(params: CreateParams): Stripe.Customer { + // Validate, insert to DB, build shape, return + } + + retrieve(id: string): Stripe.Customer { + // Query DB, build shape, return (or throw 404) + } +} +``` + +Each resource has a `build*Shape()` function that constructs the full Stripe object. The full JSON is stored in a `data` text column; key fields are indexed separately for queries. + +## State Machines + +Resources with lifecycle states validate transitions with `stateTransitionError()`: + +### Payment Intents + +``` +requires_payment_method → requires_confirmation → requires_action → succeeded + → canceled +``` + +### Setup Intents + +``` +requires_payment_method → requires_confirmation → succeeded + → canceled +``` + +### Subscriptions + +``` +trialing → active → past_due → canceled + → canceled +``` + +### Invoices + +``` +draft → open → paid + → void +``` + +## Event Emission + +Services with state changes emit events via an optional `EventService` dependency: + +```typescript +this.eventService?.emit("payment_intent.succeeded", paymentIntent); +``` + +`EventService.emit()` persists the event to DB and synchronously notifies listeners. `WebhookDeliveryService` is registered as a listener and delivers to matching webhook endpoints with HMAC-SHA256 signatures. + +## Search + +Search endpoints load all rows and filter in-memory via `parseSearchQuery()` / `matchesCondition()`. Supports Stripe's query language: + +- `email:"foo@bar.com"` — exact match +- `status~"act"` — contains +- `metadata["key"]:"value"` — metadata search +- Combine with `AND` diff --git a/docs/content/docs/getting-started/connecting-sdk.mdx b/docs/content/docs/getting-started/connecting-sdk.mdx new file mode 100644 index 0000000..636f126 --- /dev/null +++ b/docs/content/docs/getting-started/connecting-sdk.mdx @@ -0,0 +1,71 @@ +--- +title: Connecting the Stripe SDK +description: Use the official Stripe SDK with Strimulator +--- + +Point the official Stripe SDK at Strimulator — no code changes needed beyond the configuration. + +## Node.js / TypeScript + +```typescript +import Stripe from "stripe"; + +const stripe = new Stripe("sk_test_strimulator", { + host: "localhost", + port: 12111, + protocol: "http", +}); + +// Use exactly like real Stripe +const customer = await stripe.customers.create({ + email: "dev@example.com", +}); + +const product = await stripe.products.create({ + name: "Pro Plan", +}); + +const price = await stripe.prices.create({ + product: product.id, + unit_amount: 2000, + currency: "usd", + recurring: { interval: "month" }, +}); + +const subscription = await stripe.subscriptions.create({ + customer: customer.id, + items: [{ price: price.id }], +}); + +console.log(subscription.status); // "active" +``` + +## Environment Variable Approach + +If your app reads `STRIPE_SECRET_KEY`, you can switch to Strimulator without touching code: + +```bash +STRIPE_SECRET_KEY=sk_test_strimulator \ +STRIPE_API_BASE=http://localhost:12111 \ + npm run dev +``` + +## Docker Compose + +When using Docker Compose, point your app at the service name: + +```yaml +services: + strimulator: + image: ghcr.io/codeforge-tech/strimulator:latest + ports: + - "12111:12111" + + your-app: + build: . + environment: + STRIPE_SECRET_KEY: sk_test_strimulator + STRIPE_API_BASE: http://strimulator:12111 + depends_on: + - strimulator +``` diff --git a/docs/content/docs/getting-started/installation.mdx b/docs/content/docs/getting-started/installation.mdx new file mode 100644 index 0000000..4d13f92 --- /dev/null +++ b/docs/content/docs/getting-started/installation.mdx @@ -0,0 +1,58 @@ +--- +title: Installation +description: Install and run Strimulator locally or with Docker +--- + +## Prerequisites + +- [Bun](https://bun.sh) v1.0+ (for local install) +- Or [Docker](https://www.docker.com/) (for containerized setup) + +## Local Install + +```bash +git clone https://github.com/codeforge-tech/strimulator.git +cd strimulator +bun install +bun run dev +``` + +Strimulator is now running: + +- **API:** http://localhost:12111/v1/ +- **Dashboard:** http://localhost:12111/dashboard + +## Docker + +### Docker Compose (recommended) + +Add to your project's `docker-compose.yml`: + +```yaml +services: + strimulator: + image: ghcr.io/codeforge-tech/strimulator:latest + ports: + - "12111:12111" + volumes: + - strimulator-data:/data + +volumes: + strimulator-data: +``` + +### Build locally + +```bash +docker build -t strimulator . +docker run -p 12111:12111 strimulator +``` + +## Configuration + +| Environment Variable | Default | Description | +|---------------------|---------|-------------| +| `STRIMULATOR_PORT` | `12111` | Server port | +| `STRIMULATOR_DB_PATH` | `:memory:` | SQLite path. Use a file path for persistence across restarts | +| `STRIMULATOR_LOG_LEVEL` | `info` | Log verbosity | +| `STRIMULATOR_API_VERSION` | `2024-12-18` | Stripe API version in responses | diff --git a/docs/content/docs/getting-started/meta.json b/docs/content/docs/getting-started/meta.json new file mode 100644 index 0000000..cbad77e --- /dev/null +++ b/docs/content/docs/getting-started/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Getting Started", + "pages": ["installation", "quick-start", "connecting-sdk"] +} diff --git a/docs/content/docs/getting-started/quick-start.mdx b/docs/content/docs/getting-started/quick-start.mdx new file mode 100644 index 0000000..db1683d --- /dev/null +++ b/docs/content/docs/getting-started/quick-start.mdx @@ -0,0 +1,70 @@ +--- +title: Quick Start +description: Make your first API calls to Strimulator +--- + +With Strimulator running (`bun run dev`), try these requests: + +## Create a Customer + +```bash +curl -X POST http://localhost:12111/v1/customers \ + -H "Authorization: Bearer sk_test_123" \ + -d "email=hello@example.com" \ + -d "name=Jane Doe" +``` + +## Create a Product and Price + +```bash +curl -X POST http://localhost:12111/v1/products \ + -H "Authorization: Bearer sk_test_123" \ + -d "name=Pro Plan" + +# Use the product ID from the response +curl -X POST http://localhost:12111/v1/prices \ + -H "Authorization: Bearer sk_test_123" \ + -d "product=prod_xxxxx" \ + -d "unit_amount=2000" \ + -d "currency=usd" \ + -d "recurring[interval]=month" +``` + +## Create a Payment Intent + +```bash +curl -X POST http://localhost:12111/v1/payment_intents \ + -H "Authorization: Bearer sk_test_123" \ + -d "amount=2000" \ + -d "currency=usd" \ + -d "payment_method=tok_visa" \ + -d "confirm=true" +``` + +## List Resources + +```bash +curl http://localhost:12111/v1/customers \ + -H "Authorization: Bearer sk_test_123" +``` + +## Authentication + +All `/v1/` requests require a bearer token starting with `sk_test_`: + +``` +Authorization: Bearer sk_test_anything +``` + +The actual key value doesn't matter — Strimulator accepts any `sk_test_*` key. + +## Request Format + +Like real Stripe, requests use `application/x-www-form-urlencoded` (not JSON): + +```bash +curl -X POST http://localhost:12111/v1/customers \ + -H "Authorization: Bearer sk_test_123" \ + -d "email=test@example.com" \ + -d "metadata[plan]=pro" +``` diff --git a/docs/content/docs/guides/3ds-simulation.mdx b/docs/content/docs/guides/3ds-simulation.mdx new file mode 100644 index 0000000..f5cc688 --- /dev/null +++ b/docs/content/docs/guides/3ds-simulation.mdx @@ -0,0 +1,43 @@ +--- +title: 3D Secure Simulation +description: Simulate 3DS authentication challenges in payment flows +--- + +Strimulator supports simulating 3D Secure (3DS) authentication challenges for testing payment flows that require additional verification. + +## Triggering 3DS + +Use the magic token `tok_threeDSecureRequired` (a payment method with last4 `3220`) to trigger `requires_action` status on confirm: + +```typescript +const paymentIntent = await stripe.paymentIntents.create({ + amount: 2000, + currency: "usd", + payment_method: "tok_threeDSecureRequired", + confirm: true, +}); + +console.log(paymentIntent.status); // "requires_action" +console.log(paymentIntent.next_action?.type); // "use_stripe_sdk" +``` + +## Completing the 3DS Challenge + +Re-confirm the payment intent to simulate the user completing the 3DS challenge: + +```typescript +const completed = await stripe.paymentIntents.confirm(paymentIntent.id); +console.log(completed.status); // "succeeded" +``` + +## Flow Summary + +``` +Create PI with tok_threeDSecureRequired + confirm: true + → status: "requires_action" + → next_action: { type: "use_stripe_sdk" } + +Re-confirm the PI + → status: "succeeded" + → charge created +``` diff --git a/docs/content/docs/guides/dashboard.mdx b/docs/content/docs/guides/dashboard.mdx new file mode 100644 index 0000000..bf68cec --- /dev/null +++ b/docs/content/docs/guides/dashboard.mdx @@ -0,0 +1,34 @@ +--- +title: Dashboard +description: Real-time debug interface for monitoring and testing +--- + +Strimulator includes a built-in debug dashboard at http://localhost:12111/dashboard. + +## Activity Feed + +Live stream of all API requests showing method, path, status code, and timing. Uses Server-Sent Events (SSE) for real-time updates. + +## Resource Explorer + +Browse all stored objects by type. Click any object to view its full JSON representation. + +## Actions Panel + +Trigger simulated scenarios without writing code: + +| Action | Description | +|--------|-------------| +| **Fail Next Payment** | Force the next PaymentIntent confirmation to fail (`card_declined`, `insufficient_funds`, `expired_card`) | +| **Advance Test Clock** | Move a test clock forward in time, triggering subscription transitions | +| **Retry Webhook** | Re-deliver an event to a webhook endpoint | +| **Expire Payment Intent** | Force a PaymentIntent into canceled state | +| **Cycle Subscription** | Advance a subscription to the next billing period | + +## Webhooks Tab + +View registered webhook endpoints, delivery history, and retry failed deliveries. + +## Access + +The dashboard is not auth-protected — it's designed for local development use only. diff --git a/docs/content/docs/guides/idempotency.mdx b/docs/content/docs/guides/idempotency.mdx new file mode 100644 index 0000000..ba3bf7e --- /dev/null +++ b/docs/content/docs/guides/idempotency.mdx @@ -0,0 +1,41 @@ +--- +title: Idempotency +description: How Idempotency-Key works in Strimulator +--- + +Strimulator supports the `Idempotency-Key` header on POST requests, matching Stripe's behavior. + +## How It Works + +Send an `Idempotency-Key` header with any POST request. If the same key is sent again, Strimulator returns the cached response instead of creating a duplicate resource. + +```bash +curl -X POST http://localhost:12111/v1/customers \ + -H "Authorization: Bearer sk_test_123" \ + -H "Idempotency-Key: unique-key-123" \ + -d "email=test@example.com" + +# Same key → same response, no duplicate customer +curl -X POST http://localhost:12111/v1/customers \ + -H "Authorization: Bearer sk_test_123" \ + -H "Idempotency-Key: unique-key-123" \ + -d "email=test@example.com" +``` + +## SDK Usage + +The Stripe SDK automatically generates idempotency keys for most create operations. You can also specify one explicitly: + +```typescript +const customer = await stripe.customers.create( + { email: "test@example.com" }, + { idempotencyKey: "unique-key-123" }, +); +``` + +## Behavior + +- Keys are scoped to the API key and endpoint +- Cached responses are returned with the same status code and body +- Only POST requests support idempotency keys +- Keys are stored in-memory (or SQLite depending on `STRIMULATOR_DB_PATH`) diff --git a/docs/content/docs/guides/meta.json b/docs/content/docs/guides/meta.json new file mode 100644 index 0000000..8e4a6d3 --- /dev/null +++ b/docs/content/docs/guides/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Guides", + "pages": ["webhooks", "test-clocks", "3ds-simulation", "dashboard", "idempotency"] +} diff --git a/docs/content/docs/guides/test-clocks.mdx b/docs/content/docs/guides/test-clocks.mdx new file mode 100644 index 0000000..0e49dc9 --- /dev/null +++ b/docs/content/docs/guides/test-clocks.mdx @@ -0,0 +1,52 @@ +--- +title: Test Clocks +description: Simulate time advancement for subscription billing +--- + +Test clocks let you simulate the passage of time to test subscription lifecycle events — billing cycles, trial expirations, and period transitions. + +## Creating a Test Clock + +```typescript +const clock = await stripe.testHelpers.testClocks.create({ + frozen_time: Math.floor(Date.now() / 1000), + name: "Billing test", +}); +``` + +## Linking a Subscription to a Clock + +Create a customer with `test_clock`, then create subscriptions on that customer: + +```typescript +const customer = await stripe.customers.create({ + email: "test@example.com", + test_clock: clock.id, +}); + +const subscription = await stripe.subscriptions.create({ + customer: customer.id, + items: [{ price: priceId }], +}); +``` + +## Advancing Time + +Move the clock forward to trigger billing events: + +```typescript +await stripe.testHelpers.testClocks.advance(clock.id, { + frozen_time: clock.frozen_time + 30 * 24 * 60 * 60, // +30 days +}); +``` + +When advanced, Strimulator processes billing cycles for linked subscriptions: + +- Rolls subscription periods forward +- Creates and finalizes invoices +- Processes payments (creates charges) +- Handles trial-to-active transitions + +## Dashboard + +You can also advance test clocks from the dashboard's **Actions** panel without writing code. diff --git a/docs/content/docs/guides/webhooks.mdx b/docs/content/docs/guides/webhooks.mdx new file mode 100644 index 0000000..cdd243e --- /dev/null +++ b/docs/content/docs/guides/webhooks.mdx @@ -0,0 +1,63 @@ +--- +title: Webhooks +description: Register webhook endpoints and receive event notifications +--- + +Strimulator delivers webhook events just like Stripe — with HMAC-SHA256 signatures, retry logic, and the same event shapes. + +## Registering a Webhook Endpoint + +```bash +curl -X POST http://localhost:12111/v1/webhook_endpoints \ + -H "Authorization: Bearer sk_test_123" \ + -d "url=http://localhost:3000/webhooks/stripe" \ + -d "enabled_events[]=customer.created" \ + -d "enabled_events[]=payment_intent.succeeded" +``` + +Or using the SDK: + +```typescript +const endpoint = await stripe.webhookEndpoints.create({ + url: "http://localhost:3000/webhooks/stripe", + enabled_events: [ + "customer.created", + "payment_intent.succeeded", + ], +}); +``` + +Use `enabled_events: ["*"]` to receive all event types. + +## Signature Verification + +Strimulator signs webhook payloads with HMAC-SHA256, compatible with `stripe.webhooks.constructEvent()`: + +```typescript +import Stripe from "stripe"; + +const stripe = new Stripe("sk_test_strimulator", { + host: "localhost", + port: 12111, + protocol: "http", +}); + +// In your webhook handler +const event = stripe.webhooks.constructEvent( + body, + request.headers["stripe-signature"], + endpoint.secret, // from the webhook endpoint's `secret` field +); +``` + +## Retry Behavior + +When your endpoint returns a non-2xx status, Strimulator retries delivery. Failed deliveries and their status are visible in the dashboard. + +## Managing Webhooks from the Dashboard + +Open http://localhost:12111/dashboard and go to the **Webhooks** tab to: + +- View registered webhook endpoints +- See delivery history and status +- Retry failed deliveries diff --git a/docs/content/docs/index.mdx b/docs/content/docs/index.mdx new file mode 100644 index 0000000..84793a3 --- /dev/null +++ b/docs/content/docs/index.mdx @@ -0,0 +1,42 @@ +--- +title: Strimulator +description: A local Stripe emulator for development and testing +--- + +Strimulator is a drop-in local replacement for the Stripe API. It runs as a single process, stores everything in SQLite, and is compatible with the official `stripe` Node SDK. Use it to develop and test payment flows entirely offline — no Stripe account or network access required. + +**Think of it as [LocalStack](https://github.com/localstack/localstack), but for Stripe.** + +## Why Strimulator? + +- **Offline development** — No internet, no Stripe test mode, no rate limits +- **Fast feedback** — Instant responses, no network latency +- **Full control** — Trigger payment failures, advance subscriptions, simulate edge cases from the dashboard +- **SDK-compatible** — Point the official `stripe` package at localhost and it just works +- **Docker-ready** — Drop it into your docker-compose alongside Postgres, Redis, Firebase emulator, etc. + +## Supported Resources + +| Resource | Endpoints | State Machine | +|----------|-----------|:------------:| +| Customers | CRUD + list + search | | +| Products | CRUD + list + search | | +| Prices | create, retrieve, update, list | | +| Payment Methods | create, retrieve, attach, detach, list | | +| Payment Intents | create, retrieve, confirm, capture, cancel, list, search | ✓ | +| Setup Intents | create, retrieve, confirm, cancel, list | ✓ | +| Charges | retrieve, list | | +| Refunds | create, retrieve, list | | +| Subscriptions | create, retrieve, cancel, list, search | ✓ | +| Invoices | create, retrieve, finalize, pay, void, list, search | ✓ | +| Events | retrieve, list | | +| Webhook Endpoints | CRUD + list | | +| Test Clocks | create, retrieve, advance, delete, list | | + +## Additional Features + +- **Webhook delivery** with `Stripe-Signature` HMAC-SHA256 headers and retry logic +- **Search API** with Stripe's query language +- **expand[]** for one-level and nested expansion +- **Idempotency-Key** support for POST requests +- **Magic test tokens** — `tok_visa`, `tok_mastercard`, `tok_amex`, `tok_visa_debit` diff --git a/docs/content/docs/meta.json b/docs/content/docs/meta.json new file mode 100644 index 0000000..62f0331 --- /dev/null +++ b/docs/content/docs/meta.json @@ -0,0 +1,13 @@ +{ + "title": "Strimulator", + "pages": [ + "---Getting Started---", + "getting-started", + "---Guides---", + "guides", + "---API Reference---", + "api", + "---Architecture---", + "architecture" + ] +} diff --git a/docs/lib/layout.shared.tsx b/docs/lib/layout.shared.tsx new file mode 100644 index 0000000..73e5580 --- /dev/null +++ b/docs/lib/layout.shared.tsx @@ -0,0 +1,17 @@ +import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared'; + +export function baseOptions(): BaseLayoutProps { + return { + nav: { + title: 'Strimulator', + }, + links: [ + { + text: 'Documentation', + url: '/docs', + active: 'nested-url', + }, + ], + githubUrl: 'https://github.com/codeforge-tech/strimulator', + }; +} diff --git a/docs/lib/openapi.ts b/docs/lib/openapi.ts new file mode 100644 index 0000000..6e0c8ef --- /dev/null +++ b/docs/lib/openapi.ts @@ -0,0 +1,3 @@ +import { createOpenAPI } from 'fumadocs-openapi/server'; + +export const openapi = createOpenAPI(); diff --git a/docs/lib/source.ts b/docs/lib/source.ts new file mode 100644 index 0000000..0edc758 --- /dev/null +++ b/docs/lib/source.ts @@ -0,0 +1,7 @@ +import { docs } from '@/.source'; +import { loader } from 'fumadocs-core/source'; + +export const source = loader({ + baseUrl: '/docs', + source: docs.toFumadocsSource(), +}); diff --git a/docs/next-env.d.ts b/docs/next-env.d.ts new file mode 100644 index 0000000..830fb59 --- /dev/null +++ b/docs/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/docs/next.config.mjs b/docs/next.config.mjs new file mode 100644 index 0000000..4d126f1 --- /dev/null +++ b/docs/next.config.mjs @@ -0,0 +1,10 @@ +import { createMDX } from 'fumadocs-mdx/next'; + +const withMDX = createMDX(); + +/** @type {import('next').NextConfig} */ +const config = { + output: 'export', +}; + +export default withMDX(config); diff --git a/docs/openapi.json b/docs/openapi.json new file mode 100644 index 0000000..d868995 --- /dev/null +++ b/docs/openapi.json @@ -0,0 +1,3536 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Strimulator API", + "description": "Local Stripe emulator for development and testing. Strimulator faithfully emulates Stripe's REST API, letting you build and test payment integrations without hitting the real Stripe servers.", + "version": "1.0.0", + "contact": { + "name": "Strimulator", + "url": "https://github.com/codeforge-tech/strimulator" + }, + "license": { + "name": "MIT" + } + }, + "servers": [ + { + "url": "http://localhost:12111", + "description": "Local Strimulator server" + } + ], + "security": [ + { + "BearerAuth": [] + } + ], + "tags": [ + { "name": "Customers", "description": "Customer objects allow you to perform recurring charges and track payments. Strimulator emulates Stripe's customer API." }, + { "name": "Products", "description": "Products describe the items or services you offer to your customers." }, + { "name": "Prices", "description": "Prices define the unit cost, currency, and billing cycle for both one-time and recurring purchases of products." }, + { "name": "Payment Methods", "description": "Payment method objects represent a customer's payment instruments, such as credit cards. Strimulator supports the card type with magic test tokens." }, + { "name": "Payment Intents", "description": "A PaymentIntent guides you through the process of collecting a payment from your customer. Strimulator emulates the full lifecycle including 3DS simulation." }, + { "name": "Setup Intents", "description": "A SetupIntent guides you through the process of setting up a customer's payment credentials for future payments." }, + { "name": "Charges", "description": "Charge objects are created automatically when a PaymentIntent succeeds. They represent the movement of money." }, + { "name": "Refunds", "description": "Refund objects allow you to refund a charge that has been previously created." }, + { "name": "Subscriptions", "description": "Subscriptions allow you to charge a customer on a recurring basis. Strimulator supports trial periods, plan changes, and cancellation." }, + { "name": "Invoices", "description": "Invoices are statements of amounts owed by a customer. They are automatically created by subscriptions and can be finalized, paid, or voided." }, + { "name": "Events", "description": "Events are Strimulator's way of letting you know when something interesting happens. Events are created for every state change in the system." }, + { "name": "Webhook Endpoints", "description": "Configure webhook endpoints to receive event notifications via HTTP POST. Strimulator delivers webhooks with HMAC-SHA256 signatures." }, + { "name": "Test Clocks", "description": "Test clocks allow you to simulate the passage of time for testing subscription billing, trial expirations, and other time-sensitive features." } + ], + "paths": { + "/v1/customers": { + "post": { + "operationId": "createCustomer", + "tags": ["Customers"], + "summary": "Create a customer", + "description": "Creates a new customer object.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email", + "description": "Customer's email address." + }, + "name": { + "type": "string", + "description": "The customer's full name or business name." + }, + "description": { + "type": "string", + "description": "An arbitrary string attached to the object." + }, + "phone": { + "type": "string", + "description": "The customer's phone number." + }, + "metadata": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Set of key-value pairs for storing additional information." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The created customer object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Customer" } + } + } + }, + "400": { + "description": "Invalid request.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + }, + "401": { + "description": "Authentication failed.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "get": { + "operationId": "listCustomers", + "tags": ["Customers"], + "summary": "List all customers", + "description": "Returns a paginated list of customers, sorted by creation date (most recent first).", + "parameters": [ + { "$ref": "#/components/parameters/Limit" }, + { "$ref": "#/components/parameters/StartingAfter" }, + { "$ref": "#/components/parameters/EndingBefore" } + ], + "responses": { + "200": { + "description": "A paginated list of customer objects.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { "$ref": "#/components/schemas/ListResponse" }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/Customer" } + }, + "url": { "type": "string", "example": "/v1/customers" } + } + } + ] + } + } + } + } + } + } + }, + "/v1/customers/search": { + "get": { + "operationId": "searchCustomers", + "tags": ["Customers"], + "summary": "Search customers", + "description": "Search for customers using Stripe's search query language. Supports filtering by `email`, `name`, `metadata`, and other fields.", + "parameters": [ + { + "name": "query", + "in": "query", + "required": true, + "description": "The search query string using Stripe search syntax (e.g., `email:\"user@example.com\"`).", + "schema": { "type": "string" } + }, + { + "name": "limit", + "in": "query", + "description": "Maximum number of results to return (1-100, default 10).", + "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 10 } + } + ], + "responses": { + "200": { + "description": "A search result containing matching customers.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { "$ref": "#/components/schemas/SearchResult" }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/Customer" } + }, + "url": { "type": "string", "example": "/v1/customers/search" } + } + } + ] + } + } + } + } + } + } + }, + "/v1/customers/{id}": { + "get": { + "operationId": "retrieveCustomer", + "tags": ["Customers"], + "summary": "Retrieve a customer", + "description": "Retrieves a customer object by its ID.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the customer to retrieve (e.g., `cus_...`).", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "The customer object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Customer" } + } + } + }, + "404": { + "description": "Customer not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "post": { + "operationId": "updateCustomer", + "tags": ["Customers"], + "summary": "Update a customer", + "description": "Updates the specified customer by setting the values of the parameters passed. Any parameters not provided will be left unchanged.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the customer to update.", + "schema": { "type": "string" } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { "type": "string", "format": "email", "description": "Customer's email address." }, + "name": { "type": "string", "description": "The customer's full name or business name." }, + "description": { "type": "string", "description": "An arbitrary string attached to the object." }, + "phone": { "type": "string", "description": "The customer's phone number." }, + "metadata": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Set of key-value pairs for storing additional information." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The updated customer object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Customer" } + } + } + }, + "404": { + "description": "Customer not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "delete": { + "operationId": "deleteCustomer", + "tags": ["Customers"], + "summary": "Delete a customer", + "description": "Permanently deletes a customer. It cannot be undone.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the customer to delete.", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "A deletion confirmation object.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { "type": "string", "description": "The ID of the deleted customer." }, + "object": { "type": "string", "enum": ["customer"] }, + "deleted": { "type": "boolean", "enum": [true] } + }, + "required": ["id", "object", "deleted"] + } + } + } + }, + "404": { + "description": "Customer not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/products": { + "post": { + "operationId": "createProduct", + "tags": ["Products"], + "summary": "Create a product", + "description": "Creates a new product object.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["name"], + "properties": { + "name": { "type": "string", "description": "The product's name, displayed to customers." }, + "active": { "type": "boolean", "description": "Whether the product is currently available for purchase. Defaults to `true`.", "default": true }, + "description": { "type": "string", "description": "The product's description." }, + "metadata": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Set of key-value pairs for storing additional information." + }, + "url": { "type": "string", "description": "A URL of the product." }, + "statement_descriptor": { "type": "string", "description": "Descriptor for the product, shown on customer statements." }, + "unit_label": { "type": "string", "description": "A label that represents units of this product." }, + "tax_code": { "type": "string", "description": "A tax code ID." } + } + } + } + } + }, + "responses": { + "200": { + "description": "The created product object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Product" } + } + } + }, + "400": { + "description": "Invalid request (e.g., missing required `name` parameter).", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "get": { + "operationId": "listProducts", + "tags": ["Products"], + "summary": "List all products", + "description": "Returns a paginated list of products.", + "parameters": [ + { "$ref": "#/components/parameters/Limit" }, + { "$ref": "#/components/parameters/StartingAfter" }, + { "$ref": "#/components/parameters/EndingBefore" } + ], + "responses": { + "200": { + "description": "A paginated list of product objects.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { "$ref": "#/components/schemas/ListResponse" }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/Product" } + }, + "url": { "type": "string", "example": "/v1/products" } + } + } + ] + } + } + } + } + } + } + }, + "/v1/products/{id}": { + "get": { + "operationId": "retrieveProduct", + "tags": ["Products"], + "summary": "Retrieve a product", + "description": "Retrieves the details of an existing product.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the product to retrieve (e.g., `prod_...`).", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "The product object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Product" } + } + } + }, + "404": { + "description": "Product not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "post": { + "operationId": "updateProduct", + "tags": ["Products"], + "summary": "Update a product", + "description": "Updates the specified product by setting the values of the parameters passed.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the product to update.", + "schema": { "type": "string" } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { "type": "string", "description": "The product's name." }, + "active": { "type": "boolean", "description": "Whether the product is available for purchase." }, + "description": { "type": "string", "description": "The product's description." }, + "metadata": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Set of key-value pairs for storing additional information." + }, + "url": { "type": "string", "description": "A URL of the product." }, + "statement_descriptor": { "type": "string", "description": "Descriptor for the product." }, + "unit_label": { "type": "string", "description": "A label that represents units of this product." }, + "tax_code": { "type": "string", "description": "A tax code ID." } + } + } + } + } + }, + "responses": { + "200": { + "description": "The updated product object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Product" } + } + } + }, + "404": { + "description": "Product not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "delete": { + "operationId": "deleteProduct", + "tags": ["Products"], + "summary": "Delete a product", + "description": "Permanently deletes a product.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the product to delete.", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "A deletion confirmation object.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "object": { "type": "string", "enum": ["product"] }, + "deleted": { "type": "boolean", "enum": [true] } + }, + "required": ["id", "object", "deleted"] + } + } + } + }, + "404": { + "description": "Product not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/prices": { + "post": { + "operationId": "createPrice", + "tags": ["Prices"], + "summary": "Create a price", + "description": "Creates a new price for an existing product. The price can be one-time or recurring.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["currency", "product"], + "properties": { + "unit_amount": { + "type": "integer", + "description": "A positive integer in cents representing how much to charge." + }, + "currency": { + "type": "string", + "description": "Three-letter ISO currency code, in lowercase (e.g., `usd`)." + }, + "product": { + "type": "string", + "description": "The ID of the product this price belongs to (e.g., `prod_...`)." + }, + "recurring": { + "type": "object", + "description": "The recurring components of a price such as `interval` and `interval_count`.", + "properties": { + "interval": { + "type": "string", + "enum": ["day", "week", "month", "year"], + "description": "The frequency at which a subscription is billed." + }, + "interval_count": { + "type": "integer", + "minimum": 1, + "description": "The number of intervals between subscription billings. Defaults to 1.", + "default": 1 + } + }, + "required": ["interval"] + }, + "active": { "type": "boolean", "description": "Whether the price is currently active. Defaults to `true`.", "default": true }, + "nickname": { "type": "string", "description": "A brief description of the price, hidden from customers." }, + "metadata": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Set of key-value pairs for storing additional information." + }, + "lookup_key": { "type": "string", "description": "A lookup key used to retrieve prices dynamically." }, + "tax_behavior": { + "type": "string", + "enum": ["exclusive", "inclusive", "unspecified"], + "description": "Specifies whether the price is inclusive or exclusive of tax." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The created price object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Price" } + } + } + }, + "400": { + "description": "Invalid request.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "get": { + "operationId": "listPrices", + "tags": ["Prices"], + "summary": "List all prices", + "description": "Returns a paginated list of prices. Optionally filter by product.", + "parameters": [ + { "$ref": "#/components/parameters/Limit" }, + { "$ref": "#/components/parameters/StartingAfter" }, + { "$ref": "#/components/parameters/EndingBefore" }, + { + "name": "product", + "in": "query", + "description": "Only return prices belonging to this product ID.", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "A paginated list of price objects.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { "$ref": "#/components/schemas/ListResponse" }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/Price" } + }, + "url": { "type": "string", "example": "/v1/prices" } + } + } + ] + } + } + } + } + } + } + }, + "/v1/prices/{id}": { + "get": { + "operationId": "retrievePrice", + "tags": ["Prices"], + "summary": "Retrieve a price", + "description": "Retrieves the details of an existing price.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the price to retrieve (e.g., `price_...`).", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "The price object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Price" } + } + } + }, + "404": { + "description": "Price not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "post": { + "operationId": "updatePrice", + "tags": ["Prices"], + "summary": "Update a price", + "description": "Updates the specified price. Only mutable fields like `active`, `nickname`, and `metadata` can be changed.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the price to update.", + "schema": { "type": "string" } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "active": { "type": "boolean", "description": "Whether the price is currently active." }, + "nickname": { "type": "string", "description": "A brief description of the price." }, + "metadata": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Set of key-value pairs for storing additional information." + }, + "lookup_key": { "type": "string", "description": "A lookup key used to retrieve prices dynamically." }, + "tax_behavior": { + "type": "string", + "enum": ["exclusive", "inclusive", "unspecified"], + "description": "Specifies whether the price is inclusive or exclusive of tax." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The updated price object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Price" } + } + } + }, + "404": { + "description": "Price not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/payment_methods": { + "post": { + "operationId": "createPaymentMethod", + "tags": ["Payment Methods"], + "summary": "Create a payment method", + "description": "Creates a payment method object. Strimulator supports `card` type with magic test tokens like `tok_visa`, `tok_mastercard`, `tok_amex`, `tok_visa_debit`, `tok_threeDSecureRequired`, and `tok_threeDSecureOptional`.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["type"], + "properties": { + "type": { + "type": "string", + "enum": ["card"], + "description": "The type of payment method. Only `card` is supported." + }, + "card": { + "type": "object", + "description": "Card details. Provide a `token` for test cards.", + "properties": { + "token": { + "type": "string", + "description": "A magic test token (e.g., `tok_visa`, `tok_mastercard`, `tok_threeDSecureRequired`)." + }, + "number": { "type": "string", "description": "The card number." }, + "exp_month": { "type": "integer", "description": "Two-digit number representing the card's expiration month." }, + "exp_year": { "type": "integer", "description": "Four-digit number representing the card's expiration year." }, + "cvc": { "type": "string", "description": "Card security code." } + } + }, + "billing_details": { + "type": "object", + "description": "Billing information associated with the payment method.", + "properties": { + "email": { "type": "string" }, + "name": { "type": "string" }, + "phone": { "type": "string" }, + "address": { + "type": "object", + "properties": { + "line1": { "type": "string" }, + "line2": { "type": "string" }, + "city": { "type": "string" }, + "state": { "type": "string" }, + "postal_code": { "type": "string" }, + "country": { "type": "string" } + } + } + } + }, + "metadata": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Set of key-value pairs for storing additional information." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The created payment method object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/PaymentMethod" } + } + } + }, + "400": { + "description": "Invalid request.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "get": { + "operationId": "listPaymentMethods", + "tags": ["Payment Methods"], + "summary": "List payment methods", + "description": "Returns a list of payment methods for a given customer.", + "parameters": [ + { "$ref": "#/components/parameters/Limit" }, + { "$ref": "#/components/parameters/StartingAfter" }, + { "$ref": "#/components/parameters/EndingBefore" }, + { + "name": "customer", + "in": "query", + "description": "The ID of the customer whose payment methods will be listed.", + "schema": { "type": "string" } + }, + { + "name": "type", + "in": "query", + "description": "The type of payment methods to list. Defaults to `card`.", + "schema": { "type": "string", "default": "card" } + } + ], + "responses": { + "200": { + "description": "A paginated list of payment method objects.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { "$ref": "#/components/schemas/ListResponse" }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/PaymentMethod" } + }, + "url": { "type": "string", "example": "/v1/payment_methods" } + } + } + ] + } + } + } + } + } + } + }, + "/v1/payment_methods/{id}": { + "get": { + "operationId": "retrievePaymentMethod", + "tags": ["Payment Methods"], + "summary": "Retrieve a payment method", + "description": "Retrieves a payment method object.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the payment method to retrieve (e.g., `pm_...`).", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "The payment method object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/PaymentMethod" } + } + } + }, + "404": { + "description": "Payment method not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/payment_methods/{id}/attach": { + "post": { + "operationId": "attachPaymentMethod", + "tags": ["Payment Methods"], + "summary": "Attach a payment method to a customer", + "description": "Attaches a payment method object to a customer.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the payment method to attach.", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["customer"], + "properties": { + "customer": { + "type": "string", + "description": "The ID of the customer to attach the payment method to." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The attached payment method object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/PaymentMethod" } + } + } + }, + "400": { + "description": "Invalid request.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/payment_methods/{id}/detach": { + "post": { + "operationId": "detachPaymentMethod", + "tags": ["Payment Methods"], + "summary": "Detach a payment method from its customer", + "description": "Detaches a payment method object from the customer it is attached to. The payment method still exists but can no longer be used for payments.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the payment method to detach.", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "The detached payment method object (customer field set to null).", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/PaymentMethod" } + } + } + }, + "400": { + "description": "Invalid request.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/payment_intents": { + "post": { + "operationId": "createPaymentIntent", + "tags": ["Payment Intents"], + "summary": "Create a payment intent", + "description": "Creates a PaymentIntent object. Optionally set `confirm=true` with a `payment_method` to create and confirm in one step.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["amount", "currency"], + "properties": { + "amount": { + "type": "integer", + "description": "Amount intended to be collected, in the smallest currency unit (e.g., cents)." + }, + "currency": { + "type": "string", + "description": "Three-letter ISO currency code, in lowercase." + }, + "customer": { + "type": "string", + "description": "ID of the customer this PaymentIntent is for." + }, + "payment_method": { + "type": "string", + "description": "ID of the payment method to use." + }, + "confirm": { + "type": "boolean", + "description": "Set to `true` to attempt to confirm this PaymentIntent immediately.", + "default": false + }, + "capture_method": { + "type": "string", + "enum": ["automatic", "manual"], + "description": "Controls when charges will be captured. Defaults to `automatic`.", + "default": "automatic" + }, + "metadata": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Set of key-value pairs for storing additional information." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The created PaymentIntent object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/PaymentIntent" } + } + } + }, + "400": { + "description": "Invalid request.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "get": { + "operationId": "listPaymentIntents", + "tags": ["Payment Intents"], + "summary": "List payment intents", + "description": "Returns a paginated list of PaymentIntents. Optionally filter by customer.", + "parameters": [ + { "$ref": "#/components/parameters/Limit" }, + { "$ref": "#/components/parameters/StartingAfter" }, + { "$ref": "#/components/parameters/EndingBefore" }, + { + "name": "customer", + "in": "query", + "description": "Only return PaymentIntents for this customer ID.", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "A paginated list of PaymentIntent objects.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { "$ref": "#/components/schemas/ListResponse" }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/PaymentIntent" } + }, + "url": { "type": "string", "example": "/v1/payment_intents" } + } + } + ] + } + } + } + } + } + } + }, + "/v1/payment_intents/search": { + "get": { + "operationId": "searchPaymentIntents", + "tags": ["Payment Intents"], + "summary": "Search payment intents", + "description": "Search for PaymentIntents using Stripe's search query language.", + "parameters": [ + { + "name": "query", + "in": "query", + "required": true, + "description": "The search query string (e.g., `status:\"succeeded\"`).", + "schema": { "type": "string" } + }, + { + "name": "limit", + "in": "query", + "description": "Maximum number of results to return (1-100, default 10).", + "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 10 } + } + ], + "responses": { + "200": { + "description": "A search result containing matching PaymentIntents.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { "$ref": "#/components/schemas/SearchResult" }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/PaymentIntent" } + }, + "url": { "type": "string", "example": "/v1/payment_intents/search" } + } + } + ] + } + } + } + } + } + } + }, + "/v1/payment_intents/{id}": { + "get": { + "operationId": "retrievePaymentIntent", + "tags": ["Payment Intents"], + "summary": "Retrieve a payment intent", + "description": "Retrieves the details of a PaymentIntent. Supports expansion of `customer`, `payment_method`, and `latest_charge`.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the PaymentIntent to retrieve (e.g., `pi_...`).", + "schema": { "type": "string" } + }, + { "$ref": "#/components/parameters/Expand" } + ], + "responses": { + "200": { + "description": "The PaymentIntent object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/PaymentIntent" } + } + } + }, + "404": { + "description": "PaymentIntent not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "post": { + "operationId": "updatePaymentIntent", + "tags": ["Payment Intents"], + "summary": "Update a payment intent", + "description": "Updates properties on a PaymentIntent without confirming. Returns the current PaymentIntent.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the PaymentIntent to update.", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "The PaymentIntent object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/PaymentIntent" } + } + } + }, + "404": { + "description": "PaymentIntent not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/payment_intents/{id}/confirm": { + "post": { + "operationId": "confirmPaymentIntent", + "tags": ["Payment Intents"], + "summary": "Confirm a payment intent", + "description": "Confirms a PaymentIntent, transitioning it to `succeeded`, `requires_capture`, or `requires_action` (for 3DS). Payment methods with last4 `3220` (`tok_threeDSecureRequired`) trigger 3DS simulation.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the PaymentIntent to confirm.", + "schema": { "type": "string" } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "payment_method": { + "type": "string", + "description": "ID of the payment method to use for this PaymentIntent." + }, + "capture_method": { + "type": "string", + "enum": ["automatic", "manual"], + "description": "Controls when charges will be captured." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The confirmed PaymentIntent object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/PaymentIntent" } + } + } + }, + "400": { + "description": "Invalid state transition (e.g., already succeeded).", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/payment_intents/{id}/capture": { + "post": { + "operationId": "capturePaymentIntent", + "tags": ["Payment Intents"], + "summary": "Capture a payment intent", + "description": "Captures a PaymentIntent that has a status of `requires_capture`. This is used when `capture_method=manual`.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the PaymentIntent to capture.", + "schema": { "type": "string" } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "amount_to_capture": { + "type": "integer", + "description": "The amount to capture, which must be less than or equal to the original amount. Defaults to the full amount." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The captured PaymentIntent object with `status=succeeded`.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/PaymentIntent" } + } + } + }, + "400": { + "description": "Invalid state transition (e.g., not in `requires_capture` status).", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/payment_intents/{id}/cancel": { + "post": { + "operationId": "cancelPaymentIntent", + "tags": ["Payment Intents"], + "summary": "Cancel a payment intent", + "description": "Cancels a PaymentIntent. You cannot cancel a PaymentIntent with a status of `succeeded`.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the PaymentIntent to cancel.", + "schema": { "type": "string" } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "cancellation_reason": { + "type": "string", + "enum": ["duplicate", "fraudulent", "requested_by_customer", "abandoned"], + "description": "Reason for canceling the PaymentIntent." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The canceled PaymentIntent object with `status=canceled`.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/PaymentIntent" } + } + } + }, + "400": { + "description": "Invalid state transition.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/setup_intents": { + "post": { + "operationId": "createSetupIntent", + "tags": ["Setup Intents"], + "summary": "Create a setup intent", + "description": "Creates a SetupIntent object for setting up a customer's payment method for future payments.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "customer": { + "type": "string", + "description": "ID of the customer this SetupIntent is for." + }, + "payment_method": { + "type": "string", + "description": "ID of the payment method to attach." + }, + "confirm": { + "type": "boolean", + "description": "Set to `true` to attempt to confirm this SetupIntent immediately.", + "default": false + }, + "metadata": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Set of key-value pairs for storing additional information." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The created SetupIntent object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/SetupIntent" } + } + } + }, + "400": { + "description": "Invalid request.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "get": { + "operationId": "listSetupIntents", + "tags": ["Setup Intents"], + "summary": "List setup intents", + "description": "Returns a paginated list of SetupIntents.", + "parameters": [ + { "$ref": "#/components/parameters/Limit" }, + { "$ref": "#/components/parameters/StartingAfter" }, + { "$ref": "#/components/parameters/EndingBefore" } + ], + "responses": { + "200": { + "description": "A paginated list of SetupIntent objects.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { "$ref": "#/components/schemas/ListResponse" }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/SetupIntent" } + }, + "url": { "type": "string", "example": "/v1/setup_intents" } + } + } + ] + } + } + } + } + } + } + }, + "/v1/setup_intents/{id}": { + "get": { + "operationId": "retrieveSetupIntent", + "tags": ["Setup Intents"], + "summary": "Retrieve a setup intent", + "description": "Retrieves the details of a SetupIntent.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the SetupIntent to retrieve (e.g., `seti_...`).", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "The SetupIntent object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/SetupIntent" } + } + } + }, + "404": { + "description": "SetupIntent not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/setup_intents/{id}/confirm": { + "post": { + "operationId": "confirmSetupIntent", + "tags": ["Setup Intents"], + "summary": "Confirm a setup intent", + "description": "Confirms a SetupIntent, transitioning it to `succeeded`. If a `payment_method` is provided, it attaches the payment method to the customer.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the SetupIntent to confirm.", + "schema": { "type": "string" } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "payment_method": { + "type": "string", + "description": "ID of the payment method to use for this SetupIntent." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The confirmed SetupIntent object with `status=succeeded`.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/SetupIntent" } + } + } + }, + "400": { + "description": "Invalid state transition.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/setup_intents/{id}/cancel": { + "post": { + "operationId": "cancelSetupIntent", + "tags": ["Setup Intents"], + "summary": "Cancel a setup intent", + "description": "Cancels a SetupIntent. You cannot cancel a SetupIntent that has already succeeded.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the SetupIntent to cancel.", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "The canceled SetupIntent object with `status=canceled`.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/SetupIntent" } + } + } + }, + "400": { + "description": "Invalid state transition.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/charges": { + "get": { + "operationId": "listCharges", + "tags": ["Charges"], + "summary": "List all charges", + "description": "Returns a paginated list of charges. Optionally filter by payment intent.", + "parameters": [ + { "$ref": "#/components/parameters/Limit" }, + { "$ref": "#/components/parameters/StartingAfter" }, + { "$ref": "#/components/parameters/EndingBefore" }, + { + "name": "payment_intent", + "in": "query", + "description": "Only return charges for this PaymentIntent ID.", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "A paginated list of charge objects.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { "$ref": "#/components/schemas/ListResponse" }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/Charge" } + }, + "url": { "type": "string", "example": "/v1/charges" } + } + } + ] + } + } + } + } + } + } + }, + "/v1/charges/{id}": { + "get": { + "operationId": "retrieveCharge", + "tags": ["Charges"], + "summary": "Retrieve a charge", + "description": "Retrieves the details of a charge. Supports expansion of `customer` and `payment_intent`.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the charge to retrieve (e.g., `ch_...`).", + "schema": { "type": "string" } + }, + { "$ref": "#/components/parameters/Expand" } + ], + "responses": { + "200": { + "description": "The charge object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Charge" } + } + } + }, + "404": { + "description": "Charge not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/refunds": { + "post": { + "operationId": "createRefund", + "tags": ["Refunds"], + "summary": "Create a refund", + "description": "Creates a refund for a charge or payment intent. You must provide either `charge` or `payment_intent`.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "charge": { + "type": "string", + "description": "The ID of the charge to refund." + }, + "payment_intent": { + "type": "string", + "description": "The ID of the PaymentIntent to refund." + }, + "amount": { + "type": "integer", + "description": "A positive integer in cents representing how much to refund. Defaults to the full charge amount." + }, + "reason": { + "type": "string", + "enum": ["duplicate", "fraudulent", "requested_by_customer"], + "description": "The reason for the refund." + }, + "metadata": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Set of key-value pairs for storing additional information." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The created refund object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Refund" } + } + } + }, + "400": { + "description": "Invalid request (e.g., missing both charge and payment_intent).", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "get": { + "operationId": "listRefunds", + "tags": ["Refunds"], + "summary": "List all refunds", + "description": "Returns a paginated list of refunds. Optionally filter by charge or payment intent.", + "parameters": [ + { "$ref": "#/components/parameters/Limit" }, + { "$ref": "#/components/parameters/StartingAfter" }, + { "$ref": "#/components/parameters/EndingBefore" }, + { + "name": "charge", + "in": "query", + "description": "Only return refunds for this charge ID.", + "schema": { "type": "string" } + }, + { + "name": "payment_intent", + "in": "query", + "description": "Only return refunds for this PaymentIntent ID.", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "A paginated list of refund objects.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { "$ref": "#/components/schemas/ListResponse" }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/Refund" } + }, + "url": { "type": "string", "example": "/v1/refunds" } + } + } + ] + } + } + } + } + } + } + }, + "/v1/refunds/{id}": { + "get": { + "operationId": "retrieveRefund", + "tags": ["Refunds"], + "summary": "Retrieve a refund", + "description": "Retrieves the details of a refund.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the refund to retrieve (e.g., `re_...`).", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "The refund object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Refund" } + } + } + }, + "404": { + "description": "Refund not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/subscriptions": { + "post": { + "operationId": "createSubscription", + "tags": ["Subscriptions"], + "summary": "Create a subscription", + "description": "Creates a new subscription on an existing customer. Each subscription must have at least one item with a recurring price.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["customer", "items"], + "properties": { + "customer": { + "type": "string", + "description": "The ID of the customer to subscribe." + }, + "items": { + "type": "array", + "description": "A list of prices and quantities the customer is subscribed to. Use bracket notation: `items[0][price]=price_...&items[0][quantity]=1`.", + "items": { + "type": "object", + "required": ["price"], + "properties": { + "price": { "type": "string", "description": "The ID of the price to subscribe to." }, + "quantity": { "type": "integer", "description": "The quantity of the plan. Defaults to 1.", "default": 1 } + } + } + }, + "trial_period_days": { + "type": "integer", + "description": "Number of trial days before charging the customer. Sets the subscription to `trialing` status." + }, + "metadata": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Set of key-value pairs for storing additional information." + }, + "test_clock": { + "type": "string", + "description": "ID of the test clock to associate with this subscription." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The created subscription object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Subscription" } + } + } + }, + "400": { + "description": "Invalid request.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "get": { + "operationId": "listSubscriptions", + "tags": ["Subscriptions"], + "summary": "List subscriptions", + "description": "Returns a paginated list of subscriptions. Optionally filter by customer.", + "parameters": [ + { "$ref": "#/components/parameters/Limit" }, + { "$ref": "#/components/parameters/StartingAfter" }, + { "$ref": "#/components/parameters/EndingBefore" }, + { + "name": "customer", + "in": "query", + "description": "Only return subscriptions for this customer ID.", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "A paginated list of subscription objects.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { "$ref": "#/components/schemas/ListResponse" }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/Subscription" } + }, + "url": { "type": "string", "example": "/v1/subscriptions" } + } + } + ] + } + } + } + } + } + } + }, + "/v1/subscriptions/search": { + "get": { + "operationId": "searchSubscriptions", + "tags": ["Subscriptions"], + "summary": "Search subscriptions", + "description": "Search for subscriptions using Stripe's search query language.", + "parameters": [ + { + "name": "query", + "in": "query", + "required": true, + "description": "The search query string (e.g., `status:\"active\"`).", + "schema": { "type": "string" } + }, + { + "name": "limit", + "in": "query", + "description": "Maximum number of results to return (1-100, default 10).", + "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 10 } + } + ], + "responses": { + "200": { + "description": "A search result containing matching subscriptions.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { "$ref": "#/components/schemas/SearchResult" }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/Subscription" } + }, + "url": { "type": "string", "example": "/v1/subscriptions/search" } + } + } + ] + } + } + } + } + } + } + }, + "/v1/subscriptions/{id}": { + "get": { + "operationId": "retrieveSubscription", + "tags": ["Subscriptions"], + "summary": "Retrieve a subscription", + "description": "Retrieves the subscription with the given ID. Supports expansion of `customer` and `latest_invoice` (including nested `latest_invoice.payment_intent`).", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the subscription to retrieve (e.g., `sub_...`).", + "schema": { "type": "string" } + }, + { "$ref": "#/components/parameters/Expand" } + ], + "responses": { + "200": { + "description": "The subscription object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Subscription" } + } + } + }, + "404": { + "description": "Subscription not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "post": { + "operationId": "updateSubscription", + "tags": ["Subscriptions"], + "summary": "Update a subscription", + "description": "Updates an existing subscription. You can change the plan, quantity, trial end, and cancellation settings.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the subscription to update.", + "schema": { "type": "string" } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "description": "A list of subscription items, each with a price and optional quantity.", + "items": { + "type": "object", + "properties": { + "id": { "type": "string", "description": "The ID of the subscription item to update." }, + "price": { "type": "string", "description": "The ID of the price to switch to." }, + "quantity": { "type": "integer", "description": "The quantity of the plan." } + } + } + }, + "cancel_at_period_end": { + "type": "boolean", + "description": "If set to `true`, the subscription will be canceled at the end of the current period." + }, + "trial_end": { + "type": "string", + "description": "Unix timestamp or the string `now`. Sets the trial end date." + }, + "metadata": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Set of key-value pairs for storing additional information." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The updated subscription object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Subscription" } + } + } + }, + "400": { + "description": "Invalid request.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + }, + "404": { + "description": "Subscription not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "delete": { + "operationId": "cancelSubscription", + "tags": ["Subscriptions"], + "summary": "Cancel a subscription", + "description": "Cancels a customer's subscription immediately. The subscription status transitions to `canceled`.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the subscription to cancel.", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "The canceled subscription object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Subscription" } + } + } + }, + "404": { + "description": "Subscription not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/invoices": { + "post": { + "operationId": "createInvoice", + "tags": ["Invoices"], + "summary": "Create an invoice", + "description": "Creates a new invoice for a customer. Invoices are created as drafts and must be finalized before they can be paid.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["customer"], + "properties": { + "customer": { + "type": "string", + "description": "The ID of the customer to create the invoice for." + }, + "subscription": { + "type": "string", + "description": "The ID of the subscription to create the invoice for." + }, + "currency": { + "type": "string", + "description": "Three-letter ISO currency code. Defaults to `usd`." + }, + "amount_due": { + "type": "integer", + "description": "The amount due on the invoice, in cents." + }, + "metadata": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Set of key-value pairs for storing additional information." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The created invoice object with `status=draft`.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Invoice" } + } + } + }, + "400": { + "description": "Invalid request.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "get": { + "operationId": "listInvoices", + "tags": ["Invoices"], + "summary": "List all invoices", + "description": "Returns a paginated list of invoices. Optionally filter by customer or subscription.", + "parameters": [ + { "$ref": "#/components/parameters/Limit" }, + { "$ref": "#/components/parameters/StartingAfter" }, + { "$ref": "#/components/parameters/EndingBefore" }, + { + "name": "customer", + "in": "query", + "description": "Only return invoices for this customer ID.", + "schema": { "type": "string" } + }, + { + "name": "subscription", + "in": "query", + "description": "Only return invoices for this subscription ID.", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "A paginated list of invoice objects.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { "$ref": "#/components/schemas/ListResponse" }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/Invoice" } + }, + "url": { "type": "string", "example": "/v1/invoices" } + } + } + ] + } + } + } + } + } + } + }, + "/v1/invoices/search": { + "get": { + "operationId": "searchInvoices", + "tags": ["Invoices"], + "summary": "Search invoices", + "description": "Search for invoices using Stripe's search query language.", + "parameters": [ + { + "name": "query", + "in": "query", + "required": true, + "description": "The search query string (e.g., `customer:\"cus_...\"`).", + "schema": { "type": "string" } + }, + { + "name": "limit", + "in": "query", + "description": "Maximum number of results to return (1-100, default 10).", + "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 10 } + } + ], + "responses": { + "200": { + "description": "A search result containing matching invoices.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { "$ref": "#/components/schemas/SearchResult" }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/Invoice" } + }, + "url": { "type": "string", "example": "/v1/invoices/search" } + } + } + ] + } + } + } + } + } + } + }, + "/v1/invoices/{id}": { + "get": { + "operationId": "retrieveInvoice", + "tags": ["Invoices"], + "summary": "Retrieve an invoice", + "description": "Retrieves the invoice with the given ID. Supports expansion of `customer`, `subscription`, and `payment_intent`.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the invoice to retrieve (e.g., `in_...`).", + "schema": { "type": "string" } + }, + { "$ref": "#/components/parameters/Expand" } + ], + "responses": { + "200": { + "description": "The invoice object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Invoice" } + } + } + }, + "404": { + "description": "Invoice not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/invoices/{id}/finalize": { + "post": { + "operationId": "finalizeInvoice", + "tags": ["Invoices"], + "summary": "Finalize an invoice", + "description": "Finalizes a draft invoice, transitioning it to `open` status and assigning an invoice number. Once finalized, the invoice can be paid.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the invoice to finalize.", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "The finalized invoice object with `status=open`.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Invoice" } + } + } + }, + "400": { + "description": "Invalid state transition (e.g., invoice is not a draft).", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/invoices/{id}/pay": { + "post": { + "operationId": "payInvoice", + "tags": ["Invoices"], + "summary": "Pay an invoice", + "description": "Pays an open invoice, transitioning it to `paid` status.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the invoice to pay.", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "The paid invoice object with `status=paid`.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Invoice" } + } + } + }, + "400": { + "description": "Invalid state transition (e.g., invoice is not open).", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/invoices/{id}/void": { + "post": { + "operationId": "voidInvoice", + "tags": ["Invoices"], + "summary": "Void an invoice", + "description": "Voids an open invoice, transitioning it to `void` status. A voided invoice can no longer be paid.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the invoice to void.", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "The voided invoice object with `status=void`.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Invoice" } + } + } + }, + "400": { + "description": "Invalid state transition.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/events": { + "get": { + "operationId": "listEvents", + "tags": ["Events"], + "summary": "List all events", + "description": "Returns a paginated list of events, sorted by creation date (most recent first). Optionally filter by event type.", + "parameters": [ + { "$ref": "#/components/parameters/Limit" }, + { "$ref": "#/components/parameters/StartingAfter" }, + { "$ref": "#/components/parameters/EndingBefore" }, + { + "name": "type", + "in": "query", + "description": "Filter events by type (e.g., `customer.created`, `payment_intent.succeeded`).", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "A paginated list of event objects.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { "$ref": "#/components/schemas/ListResponse" }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/Event" } + }, + "url": { "type": "string", "example": "/v1/events" } + } + } + ] + } + } + } + } + } + } + }, + "/v1/events/{id}": { + "get": { + "operationId": "retrieveEvent", + "tags": ["Events"], + "summary": "Retrieve an event", + "description": "Retrieves the details of an event.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the event to retrieve (e.g., `evt_...`).", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "The event object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Event" } + } + } + }, + "404": { + "description": "Event not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/webhook_endpoints": { + "post": { + "operationId": "createWebhookEndpoint", + "tags": ["Webhook Endpoints"], + "summary": "Create a webhook endpoint", + "description": "Creates a webhook endpoint that listens to specific events. Strimulator will POST event payloads to the configured URL with HMAC-SHA256 signatures.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["url", "enabled_events"], + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "The URL of the webhook endpoint." + }, + "enabled_events": { + "type": "array", + "items": { "type": "string" }, + "description": "The list of event types to listen for (e.g., `[\"customer.created\", \"payment_intent.succeeded\"]`). Use `[\"*\"]` to listen to all events." + }, + "description": { + "type": "string", + "description": "An optional description of what the webhook is used for." + }, + "metadata": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Set of key-value pairs for storing additional information." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The created webhook endpoint object. The response includes the webhook `secret` needed for signature verification.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/WebhookEndpoint" } + } + } + }, + "400": { + "description": "Invalid request (e.g., missing url or enabled_events).", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "get": { + "operationId": "listWebhookEndpoints", + "tags": ["Webhook Endpoints"], + "summary": "List webhook endpoints", + "description": "Returns a paginated list of webhook endpoints.", + "parameters": [ + { "$ref": "#/components/parameters/Limit" }, + { "$ref": "#/components/parameters/StartingAfter" }, + { "$ref": "#/components/parameters/EndingBefore" } + ], + "responses": { + "200": { + "description": "A paginated list of webhook endpoint objects.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { "$ref": "#/components/schemas/ListResponse" }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/WebhookEndpoint" } + }, + "url": { "type": "string", "example": "/v1/webhook_endpoints" } + } + } + ] + } + } + } + } + } + } + }, + "/v1/webhook_endpoints/{id}": { + "get": { + "operationId": "retrieveWebhookEndpoint", + "tags": ["Webhook Endpoints"], + "summary": "Retrieve a webhook endpoint", + "description": "Retrieves the webhook endpoint with the given ID.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the webhook endpoint to retrieve (e.g., `we_...`).", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "The webhook endpoint object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/WebhookEndpoint" } + } + } + }, + "404": { + "description": "Webhook endpoint not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "delete": { + "operationId": "deleteWebhookEndpoint", + "tags": ["Webhook Endpoints"], + "summary": "Delete a webhook endpoint", + "description": "Deletes a webhook endpoint. Strimulator will stop sending events to this URL.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the webhook endpoint to delete.", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "A deletion confirmation object.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "object": { "type": "string", "enum": ["webhook_endpoint"] }, + "deleted": { "type": "boolean", "enum": [true] } + }, + "required": ["id", "object", "deleted"] + } + } + } + }, + "404": { + "description": "Webhook endpoint not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/test_helpers/test_clocks": { + "post": { + "operationId": "createTestClock", + "tags": ["Test Clocks"], + "summary": "Create a test clock", + "description": "Creates a new test clock with the specified frozen time. Use test clocks to simulate the passage of time for subscription billing and trial expirations.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["frozen_time"], + "properties": { + "frozen_time": { + "type": "integer", + "description": "The initial frozen time as a Unix timestamp." + }, + "name": { + "type": "string", + "description": "A human-readable name for the test clock." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The created test clock object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/TestClock" } + } + } + }, + "400": { + "description": "Invalid request (e.g., missing frozen_time).", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "get": { + "operationId": "listTestClocks", + "tags": ["Test Clocks"], + "summary": "List test clocks", + "description": "Returns a paginated list of test clocks.", + "parameters": [ + { "$ref": "#/components/parameters/Limit" }, + { "$ref": "#/components/parameters/StartingAfter" }, + { "$ref": "#/components/parameters/EndingBefore" } + ], + "responses": { + "200": { + "description": "A paginated list of test clock objects.", + "content": { + "application/json": { + "schema": { + "allOf": [ + { "$ref": "#/components/schemas/ListResponse" }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/TestClock" } + }, + "url": { "type": "string", "example": "/v1/test_helpers/test_clocks" } + } + } + ] + } + } + } + } + } + } + }, + "/v1/test_helpers/test_clocks/{id}": { + "get": { + "operationId": "retrieveTestClock", + "tags": ["Test Clocks"], + "summary": "Retrieve a test clock", + "description": "Retrieves a test clock.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the test clock to retrieve (e.g., `clock_...`).", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "The test clock object.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/TestClock" } + } + } + }, + "404": { + "description": "Test clock not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + }, + "delete": { + "operationId": "deleteTestClock", + "tags": ["Test Clocks"], + "summary": "Delete a test clock", + "description": "Deletes a test clock and all associated resources.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the test clock to delete.", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "A deletion confirmation object.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "object": { "type": "string", "enum": ["test_helpers.test_clock"] }, + "deleted": { "type": "boolean", "enum": [true] } + }, + "required": ["id", "object", "deleted"] + } + } + } + }, + "404": { + "description": "Test clock not found.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + }, + "/v1/test_helpers/test_clocks/{id}/advance": { + "post": { + "operationId": "advanceTestClock", + "tags": ["Test Clocks"], + "summary": "Advance a test clock", + "description": "Advances a test clock's frozen time forward. This triggers billing cycle processing for any linked subscriptions, including rolling periods, creating invoices, and processing payments.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the test clock to advance.", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["frozen_time"], + "properties": { + "frozen_time": { + "type": "integer", + "description": "The new frozen time as a Unix timestamp. Must be greater than the current frozen time." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The updated test clock object with the new frozen time.", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/TestClock" } + } + } + }, + "400": { + "description": "Invalid request (e.g., frozen_time is not in the future).", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" } + } + } + } + } + } + } + }, + "components": { + "securitySchemes": { + "BearerAuth": { + "type": "http", + "scheme": "bearer", + "description": "Authenticate with a test API key. Use any string starting with `sk_test_` (e.g., `sk_test_12345`)." + } + }, + "parameters": { + "Limit": { + "name": "limit", + "in": "query", + "description": "A limit on the number of objects to be returned, between 1 and 100. Default is 10.", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 10 + } + }, + "StartingAfter": { + "name": "starting_after", + "in": "query", + "description": "A cursor for pagination. `starting_after` is an object ID that defines your place in the list. Retrieves the next page of results after this object.", + "schema": { "type": "string" } + }, + "EndingBefore": { + "name": "ending_before", + "in": "query", + "description": "A cursor for pagination. `ending_before` is an object ID that defines your place in the list. Retrieves the previous page of results before this object.", + "schema": { "type": "string" } + }, + "Expand": { + "name": "expand[]", + "in": "query", + "description": "Specifies which fields in the response should be expanded. Supports dot notation for nested expansion (e.g., `latest_invoice.payment_intent`).", + "schema": { + "type": "array", + "items": { "type": "string" } + }, + "explode": true + } + }, + "schemas": { + "Error": { + "type": "object", + "description": "An error response from the API.", + "required": ["error"], + "properties": { + "error": { + "type": "object", + "required": ["type", "message"], + "properties": { + "type": { + "type": "string", + "description": "The type of error (e.g., `invalid_request_error`, `authentication_error`, `card_error`).", + "enum": ["invalid_request_error", "authentication_error", "card_error"] + }, + "message": { + "type": "string", + "description": "A human-readable message providing more details about the error." + }, + "code": { + "type": "string", + "nullable": true, + "description": "For some errors (e.g., `resource_missing`, `payment_intent_unexpected_state`), a short string indicating the error code." + }, + "param": { + "type": "string", + "nullable": true, + "description": "If the error is parameter-specific, the parameter related to the error." + }, + "decline_code": { + "type": "string", + "nullable": true, + "description": "For card errors, the decline code from the issuer." + } + } + } + } + }, + "ListResponse": { + "type": "object", + "description": "A list of objects with cursor-based pagination.", + "required": ["object", "data", "has_more", "url"], + "properties": { + "object": { + "type": "string", + "enum": ["list"], + "description": "String representing the object's type. Always `list`." + }, + "data": { + "type": "array", + "items": {}, + "description": "An array of the requested objects." + }, + "has_more": { + "type": "boolean", + "description": "Whether there are more results available after this set." + }, + "url": { + "type": "string", + "description": "The URL where this list can be accessed." + } + } + }, + "SearchResult": { + "type": "object", + "description": "A search result containing matching objects.", + "required": ["object", "data", "has_more", "total_count", "url", "next_page"], + "properties": { + "object": { + "type": "string", + "enum": ["search_result"], + "description": "String representing the object's type. Always `search_result`." + }, + "data": { + "type": "array", + "items": {}, + "description": "An array of the matching objects." + }, + "has_more": { + "type": "boolean", + "description": "Whether there are more results available." + }, + "total_count": { + "type": "integer", + "description": "The total number of objects that match the query." + }, + "url": { + "type": "string", + "description": "The URL where this search can be accessed." + }, + "next_page": { + "type": "string", + "nullable": true, + "description": "A cursor for the next page of results. Always `null` in Strimulator." + } + } + }, + "Customer": { + "type": "object", + "description": "A customer object represents a customer of your business.", + "required": ["id", "object", "created", "livemode", "metadata"], + "properties": { + "id": { "type": "string", "description": "Unique identifier for the object (e.g., `cus_...`).", "example": "cus_abc123def456" }, + "object": { "type": "string", "enum": ["customer"], "description": "String representing the object's type." }, + "address": { "type": "object", "nullable": true, "description": "The customer's address." }, + "balance": { "type": "integer", "description": "Current balance on the customer." }, + "created": { "type": "integer", "description": "Time at which the object was created (Unix timestamp)." }, + "currency": { "type": "string", "nullable": true, "description": "The currency the customer can be charged in." }, + "default_source": { "type": "string", "nullable": true, "description": "ID of the default payment source." }, + "delinquent": { "type": "boolean", "description": "Whether the customer currently has an unpaid invoice." }, + "description": { "type": "string", "nullable": true, "description": "An arbitrary string attached to the object." }, + "discount": { "type": "object", "nullable": true, "description": "Discount object currently applied to the customer." }, + "email": { "type": "string", "nullable": true, "description": "The customer's email address." }, + "invoice_prefix": { "type": "string", "description": "The prefix for the customer used to generate unique invoice numbers." }, + "invoice_settings": { + "type": "object", + "description": "Default invoice settings for the customer.", + "properties": { + "custom_fields": { "type": "array", "nullable": true, "items": {} }, + "default_payment_method": { "type": "string", "nullable": true }, + "footer": { "type": "string", "nullable": true }, + "rendering_options": { "type": "object", "nullable": true } + } + }, + "livemode": { "type": "boolean", "description": "Always `false` in Strimulator." }, + "metadata": { "type": "object", "additionalProperties": { "type": "string" }, "description": "Set of key-value pairs attached to the object." }, + "name": { "type": "string", "nullable": true, "description": "The customer's full name or business name." }, + "phone": { "type": "string", "nullable": true, "description": "The customer's phone number." }, + "preferred_locales": { "type": "array", "items": { "type": "string" }, "description": "Customer's preferred locales." }, + "shipping": { "type": "object", "nullable": true, "description": "Shipping information for the customer." }, + "tax_exempt": { "type": "string", "enum": ["none", "exempt", "reverse"], "description": "Tax exemption status." }, + "test_clock": { "type": "string", "nullable": true, "description": "ID of the test clock this customer belongs to." } + } + }, + "Product": { + "type": "object", + "description": "A product represents an item or service you sell.", + "required": ["id", "object", "active", "created", "livemode", "metadata", "name"], + "properties": { + "id": { "type": "string", "description": "Unique identifier for the object (e.g., `prod_...`).", "example": "prod_abc123def456" }, + "object": { "type": "string", "enum": ["product"], "description": "String representing the object's type." }, + "active": { "type": "boolean", "description": "Whether the product is currently available for purchase." }, + "created": { "type": "integer", "description": "Time at which the object was created (Unix timestamp)." }, + "default_price": { "type": "string", "nullable": true, "description": "The ID of the default price for this product." }, + "description": { "type": "string", "nullable": true, "description": "The product's description." }, + "images": { "type": "array", "items": { "type": "string" }, "description": "A list of image URLs for this product." }, + "livemode": { "type": "boolean", "description": "Always `false` in Strimulator." }, + "metadata": { "type": "object", "additionalProperties": { "type": "string" }, "description": "Set of key-value pairs attached to the object." }, + "name": { "type": "string", "description": "The product's name, displayed to customers." }, + "package_dimensions": { "type": "object", "nullable": true, "description": "Package dimensions for shipping." }, + "shippable": { "type": "boolean", "nullable": true, "description": "Whether the product is shippable." }, + "statement_descriptor": { "type": "string", "nullable": true, "description": "Descriptor shown on customer statements." }, + "tax_code": { "type": "string", "nullable": true, "description": "A tax code ID." }, + "type": { "type": "string", "enum": ["service", "good"], "description": "The type of the product. Always `service` in Strimulator." }, + "unit_label": { "type": "string", "nullable": true, "description": "A label that represents units of this product." }, + "updated": { "type": "integer", "description": "Time at which the object was last updated (Unix timestamp)." }, + "url": { "type": "string", "nullable": true, "description": "A URL of the product." } + } + }, + "Price": { + "type": "object", + "description": "A price defines the unit cost, currency, and billing cycle for a product.", + "required": ["id", "object", "active", "created", "currency", "livemode", "metadata", "product", "type"], + "properties": { + "id": { "type": "string", "description": "Unique identifier for the object (e.g., `price_...`).", "example": "price_abc123def456" }, + "object": { "type": "string", "enum": ["price"], "description": "String representing the object's type." }, + "active": { "type": "boolean", "description": "Whether the price is currently active." }, + "billing_scheme": { "type": "string", "enum": ["per_unit", "tiered"], "description": "Describes how to compute the price per period." }, + "created": { "type": "integer", "description": "Time at which the object was created (Unix timestamp)." }, + "currency": { "type": "string", "description": "Three-letter ISO currency code, in lowercase." }, + "custom_unit_amount": { "type": "object", "nullable": true, "description": "Custom unit amount settings." }, + "livemode": { "type": "boolean", "description": "Always `false` in Strimulator." }, + "lookup_key": { "type": "string", "nullable": true, "description": "A lookup key used to retrieve prices dynamically." }, + "metadata": { "type": "object", "additionalProperties": { "type": "string" }, "description": "Set of key-value pairs attached to the object." }, + "nickname": { "type": "string", "nullable": true, "description": "A brief description of the price." }, + "product": { "type": "string", "description": "The ID of the product this price is for." }, + "recurring": { + "type": "object", + "nullable": true, + "description": "The recurring components of a price. Only present for recurring prices.", + "properties": { + "interval": { "type": "string", "enum": ["day", "week", "month", "year"], "description": "The billing interval." }, + "interval_count": { "type": "integer", "description": "The number of intervals between billings." }, + "usage_type": { "type": "string", "enum": ["licensed", "metered"], "description": "How the quantity per period is determined." }, + "aggregate_usage": { "type": "string", "nullable": true }, + "trial_period_days": { "type": "integer", "nullable": true }, + "meter": { "type": "string", "nullable": true } + } + }, + "tax_behavior": { "type": "string", "nullable": true, "enum": ["exclusive", "inclusive", "unspecified"], "description": "Tax behavior of the price." }, + "tiers_mode": { "type": "string", "nullable": true, "description": "The tiers mode." }, + "transform_quantity": { "type": "object", "nullable": true, "description": "Quantity transformation settings." }, + "type": { "type": "string", "enum": ["one_time", "recurring"], "description": "Whether the price is one-time or recurring." }, + "unit_amount": { "type": "integer", "nullable": true, "description": "The unit amount in cents." }, + "unit_amount_decimal": { "type": "string", "nullable": true, "description": "The unit amount in cents as a decimal string." } + } + }, + "PaymentMethod": { + "type": "object", + "description": "A payment method represents a customer's payment instrument (e.g., a credit card).", + "required": ["id", "object", "created", "livemode", "type"], + "properties": { + "id": { "type": "string", "description": "Unique identifier for the object (e.g., `pm_...`).", "example": "pm_abc123def456" }, + "object": { "type": "string", "enum": ["payment_method"], "description": "String representing the object's type." }, + "billing_details": { + "type": "object", + "description": "Billing information associated with the payment method.", + "properties": { + "address": { "type": "object", "nullable": true }, + "email": { "type": "string", "nullable": true }, + "name": { "type": "string", "nullable": true }, + "phone": { "type": "string", "nullable": true } + } + }, + "card": { + "type": "object", + "description": "Card details (present when type is `card`).", + "properties": { + "brand": { "type": "string", "description": "Card brand (e.g., `visa`, `mastercard`, `amex`)." }, + "checks": { + "type": "object", + "properties": { + "address_line1_check": { "type": "string", "nullable": true }, + "address_postal_code_check": { "type": "string", "nullable": true }, + "cvc_check": { "type": "string", "nullable": true } + } + }, + "country": { "type": "string", "description": "Two-letter ISO code for the card's issuing country." }, + "display_brand": { "type": "string", "description": "The display brand of the card." }, + "exp_month": { "type": "integer", "description": "Two-digit number representing the card's expiration month." }, + "exp_year": { "type": "integer", "description": "Four-digit number representing the card's expiration year." }, + "fingerprint": { "type": "string", "description": "Uniquely identifies this particular card number." }, + "funding": { "type": "string", "enum": ["credit", "debit", "prepaid", "unknown"], "description": "Card funding type." }, + "generated_from": { "type": "object", "nullable": true }, + "last4": { "type": "string", "description": "The last four digits of the card." }, + "networks": { + "type": "object", + "properties": { + "available": { "type": "array", "items": { "type": "string" } }, + "preferred": { "type": "string", "nullable": true } + } + }, + "three_d_secure_usage": { + "type": "object", + "properties": { + "supported": { "type": "boolean" } + } + }, + "wallet": { "type": "object", "nullable": true } + } + }, + "created": { "type": "integer", "description": "Time at which the object was created (Unix timestamp)." }, + "customer": { "type": "string", "nullable": true, "description": "The ID of the customer to which this payment method is attached, or `null`." }, + "livemode": { "type": "boolean", "description": "Always `false` in Strimulator." }, + "metadata": { "type": "object", "additionalProperties": { "type": "string" }, "description": "Set of key-value pairs attached to the object." }, + "type": { "type": "string", "enum": ["card"], "description": "The type of the payment method. Only `card` is supported." } + } + }, + "PaymentIntent": { + "type": "object", + "description": "A PaymentIntent guides you through the process of collecting a payment from your customer.", + "required": ["id", "object", "amount", "currency", "created", "livemode", "metadata", "status"], + "properties": { + "id": { "type": "string", "description": "Unique identifier for the object (e.g., `pi_...`).", "example": "pi_abc123def456" }, + "object": { "type": "string", "enum": ["payment_intent"], "description": "String representing the object's type." }, + "amount": { "type": "integer", "description": "Amount intended to be collected, in cents." }, + "amount_capturable": { "type": "integer", "description": "Amount that can be captured from this PaymentIntent." }, + "amount_received": { "type": "integer", "description": "Amount that was collected." }, + "automatic_payment_methods": { "type": "object", "nullable": true }, + "canceled_at": { "type": "integer", "nullable": true, "description": "Time at which the PaymentIntent was canceled (Unix timestamp)." }, + "cancellation_reason": { "type": "string", "nullable": true, "description": "Reason for cancellation." }, + "capture_method": { "type": "string", "enum": ["automatic", "manual"], "description": "Controls when the funds will be captured." }, + "client_secret": { "type": "string", "description": "The client secret for this PaymentIntent, used in client-side integrations." }, + "confirmation_method": { "type": "string", "enum": ["automatic", "manual"], "description": "How the PaymentIntent was confirmed." }, + "created": { "type": "integer", "description": "Time at which the object was created (Unix timestamp)." }, + "currency": { "type": "string", "description": "Three-letter ISO currency code, in lowercase." }, + "customer": { "type": "string", "nullable": true, "description": "ID of the customer this PaymentIntent is for." }, + "description": { "type": "string", "nullable": true, "description": "An arbitrary string attached to the object." }, + "last_payment_error": { + "type": "object", + "nullable": true, + "description": "The payment error encountered in the previous payment attempt.", + "properties": { + "type": { "type": "string" }, + "message": { "type": "string" }, + "code": { "type": "string" }, + "decline_code": { "type": "string" }, + "charge": { "type": "string" } + } + }, + "latest_charge": { "type": "string", "nullable": true, "description": "The ID of the most recent charge created by this PaymentIntent." }, + "livemode": { "type": "boolean", "description": "Always `false` in Strimulator." }, + "metadata": { "type": "object", "additionalProperties": { "type": "string" }, "description": "Set of key-value pairs attached to the object." }, + "next_action": { + "type": "object", + "nullable": true, + "description": "If present, this property tells you what actions you need to take for the customer to continue the payment (e.g., 3DS authentication)." + }, + "on_behalf_of": { "type": "string", "nullable": true }, + "payment_method": { "type": "string", "nullable": true, "description": "ID of the payment method used in this PaymentIntent." }, + "payment_method_options": { "type": "object", "description": "Payment method-specific configuration." }, + "payment_method_types": { "type": "array", "items": { "type": "string" }, "description": "The list of payment method types that this PaymentIntent can use." }, + "processing": { "type": "object", "nullable": true }, + "receipt_email": { "type": "string", "nullable": true }, + "setup_future_usage": { "type": "string", "nullable": true }, + "shipping": { "type": "object", "nullable": true }, + "statement_descriptor": { "type": "string", "nullable": true }, + "statement_descriptor_suffix": { "type": "string", "nullable": true }, + "status": { + "type": "string", + "enum": ["requires_payment_method", "requires_confirmation", "requires_action", "processing", "requires_capture", "canceled", "succeeded"], + "description": "Status of this PaymentIntent." + }, + "transfer_data": { "type": "object", "nullable": true }, + "transfer_group": { "type": "string", "nullable": true } + } + }, + "SetupIntent": { + "type": "object", + "description": "A SetupIntent guides you through the process of setting up a customer's payment credentials for future payments.", + "required": ["id", "object", "created", "livemode", "metadata", "status"], + "properties": { + "id": { "type": "string", "description": "Unique identifier for the object (e.g., `seti_...`).", "example": "seti_abc123def456" }, + "object": { "type": "string", "enum": ["setup_intent"], "description": "String representing the object's type." }, + "application": { "type": "string", "nullable": true }, + "automatic_payment_methods": { "type": "object", "nullable": true }, + "cancellation_reason": { "type": "string", "nullable": true, "description": "Reason for cancellation." }, + "client_secret": { "type": "string", "description": "The client secret for this SetupIntent." }, + "created": { "type": "integer", "description": "Time at which the object was created (Unix timestamp)." }, + "customer": { "type": "string", "nullable": true, "description": "ID of the customer this SetupIntent is for." }, + "description": { "type": "string", "nullable": true }, + "last_setup_error": { "type": "object", "nullable": true, "description": "The error encountered in the previous setup attempt." }, + "latest_attempt": { "type": "string", "nullable": true }, + "livemode": { "type": "boolean", "description": "Always `false` in Strimulator." }, + "mandate": { "type": "string", "nullable": true }, + "metadata": { "type": "object", "additionalProperties": { "type": "string" }, "description": "Set of key-value pairs attached to the object." }, + "next_action": { "type": "object", "nullable": true }, + "on_behalf_of": { "type": "string", "nullable": true }, + "payment_method": { "type": "string", "nullable": true, "description": "ID of the payment method attached to this SetupIntent." }, + "payment_method_options": { "type": "object", "description": "Payment method-specific configuration." }, + "payment_method_types": { "type": "array", "items": { "type": "string" }, "description": "The list of payment method types supported." }, + "single_use_mandate": { "type": "string", "nullable": true }, + "status": { + "type": "string", + "enum": ["requires_payment_method", "requires_confirmation", "requires_action", "canceled", "succeeded"], + "description": "Status of this SetupIntent." + }, + "usage": { "type": "string", "enum": ["off_session", "on_session"], "description": "Indicates the future usage of this SetupIntent's payment method." } + } + }, + "Charge": { + "type": "object", + "description": "A charge represents a payment of money. Charges are created automatically when a PaymentIntent succeeds.", + "required": ["id", "object", "amount", "created", "currency", "livemode", "metadata", "status"], + "properties": { + "id": { "type": "string", "description": "Unique identifier for the object (e.g., `ch_...`).", "example": "ch_abc123def456" }, + "object": { "type": "string", "enum": ["charge"], "description": "String representing the object's type." }, + "amount": { "type": "integer", "description": "Amount charged in cents." }, + "amount_captured": { "type": "integer", "description": "Amount captured in cents." }, + "amount_refunded": { "type": "integer", "description": "Amount refunded in cents." }, + "balance_transaction": { "type": "string", "nullable": true }, + "billing_details": { + "type": "object", + "description": "Billing information associated with the payment method.", + "properties": { + "address": { "type": "object", "nullable": true }, + "email": { "type": "string", "nullable": true }, + "name": { "type": "string", "nullable": true }, + "phone": { "type": "string", "nullable": true } + } + }, + "calculated_statement_descriptor": { "type": "string", "description": "The full statement descriptor." }, + "captured": { "type": "boolean", "description": "Whether the charge has been captured." }, + "created": { "type": "integer", "description": "Time at which the object was created (Unix timestamp)." }, + "currency": { "type": "string", "description": "Three-letter ISO currency code." }, + "customer": { "type": "string", "nullable": true, "description": "ID of the customer this charge is for." }, + "description": { "type": "string", "nullable": true }, + "disputed": { "type": "boolean", "description": "Whether the charge has been disputed." }, + "failure_code": { "type": "string", "nullable": true, "description": "Error code explaining reason for charge failure." }, + "failure_message": { "type": "string", "nullable": true, "description": "Message explaining charge failure." }, + "invoice": { "type": "string", "nullable": true, "description": "ID of the invoice this charge is for." }, + "livemode": { "type": "boolean", "description": "Always `false` in Strimulator." }, + "metadata": { "type": "object", "additionalProperties": { "type": "string" }, "description": "Set of key-value pairs attached to the object." }, + "outcome": { + "type": "object", + "description": "Details about the charge outcome.", + "properties": { + "network_status": { "type": "string", "enum": ["approved_by_network", "declined_by_network"] }, + "reason": { "type": "string", "nullable": true }, + "risk_level": { "type": "string" }, + "risk_score": { "type": "integer" }, + "seller_message": { "type": "string" }, + "type": { "type": "string", "enum": ["authorized", "issuer_declined"] } + } + }, + "paid": { "type": "boolean", "description": "Whether the charge succeeded." }, + "payment_intent": { "type": "string", "nullable": true, "description": "ID of the PaymentIntent that created this charge." }, + "payment_method": { "type": "string", "nullable": true, "description": "ID of the payment method used." }, + "refunded": { "type": "boolean", "description": "Whether the charge has been fully refunded." }, + "refunds": { + "type": "object", + "description": "A list of refunds that have been applied to the charge.", + "properties": { + "object": { "type": "string", "enum": ["list"] }, + "data": { "type": "array", "items": { "$ref": "#/components/schemas/Refund" } }, + "has_more": { "type": "boolean" }, + "url": { "type": "string" } + } + }, + "status": { "type": "string", "enum": ["succeeded", "pending", "failed"], "description": "The status of the charge." } + } + }, + "Refund": { + "type": "object", + "description": "A refund represents the return of funds to a customer.", + "required": ["id", "object", "amount", "created", "currency", "metadata", "status"], + "properties": { + "id": { "type": "string", "description": "Unique identifier for the object (e.g., `re_...`).", "example": "re_abc123def456" }, + "object": { "type": "string", "enum": ["refund"], "description": "String representing the object's type." }, + "amount": { "type": "integer", "description": "Amount refunded in cents." }, + "balance_transaction": { "type": "string", "nullable": true }, + "charge": { "type": "string", "description": "ID of the charge that was refunded." }, + "created": { "type": "integer", "description": "Time at which the object was created (Unix timestamp)." }, + "currency": { "type": "string", "description": "Three-letter ISO currency code." }, + "metadata": { "type": "object", "additionalProperties": { "type": "string" }, "description": "Set of key-value pairs attached to the object." }, + "payment_intent": { "type": "string", "nullable": true, "description": "ID of the PaymentIntent that was refunded." }, + "reason": { "type": "string", "nullable": true, "enum": ["duplicate", "fraudulent", "requested_by_customer", null], "description": "Reason for the refund." }, + "receipt_number": { "type": "string", "nullable": true }, + "source_transfer_reversal": { "type": "string", "nullable": true }, + "status": { "type": "string", "enum": ["succeeded", "pending", "failed", "canceled"], "description": "Status of the refund." }, + "transfer_reversal": { "type": "string", "nullable": true } + } + }, + "Subscription": { + "type": "object", + "description": "A subscription allows you to charge a customer on a recurring basis.", + "required": ["id", "object", "created", "currency", "customer", "livemode", "metadata", "status"], + "properties": { + "id": { "type": "string", "description": "Unique identifier for the object (e.g., `sub_...`).", "example": "sub_abc123def456" }, + "object": { "type": "string", "enum": ["subscription"], "description": "String representing the object's type." }, + "billing_cycle_anchor": { "type": "integer", "description": "Determines the date of the first full invoice." }, + "cancel_at": { "type": "integer", "nullable": true, "description": "Time at which the subscription will be automatically canceled (Unix timestamp)." }, + "cancel_at_period_end": { "type": "boolean", "description": "Whether the subscription will be canceled at the end of the current period." }, + "canceled_at": { "type": "integer", "nullable": true, "description": "Time at which the subscription was canceled (Unix timestamp)." }, + "collection_method": { "type": "string", "enum": ["charge_automatically", "send_invoice"], "description": "How the subscription is collected." }, + "created": { "type": "integer", "description": "Time at which the object was created (Unix timestamp)." }, + "currency": { "type": "string", "description": "Three-letter ISO currency code." }, + "current_period_end": { "type": "integer", "description": "End of the current period (Unix timestamp)." }, + "current_period_start": { "type": "integer", "description": "Start of the current period (Unix timestamp)." }, + "customer": { "type": "string", "description": "ID of the customer who owns the subscription." }, + "default_payment_method": { "type": "string", "nullable": true, "description": "ID of the default payment method." }, + "ended_at": { "type": "integer", "nullable": true, "description": "Time at which the subscription ended (Unix timestamp)." }, + "items": { + "type": "object", + "description": "List of subscription items, each with an assigned price.", + "properties": { + "object": { "type": "string", "enum": ["list"] }, + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/SubscriptionItem" } + }, + "has_more": { "type": "boolean" }, + "url": { "type": "string" } + } + }, + "latest_invoice": { "type": "string", "nullable": true, "description": "The most recent invoice this subscription has generated." }, + "livemode": { "type": "boolean", "description": "Always `false` in Strimulator." }, + "metadata": { "type": "object", "additionalProperties": { "type": "string" }, "description": "Set of key-value pairs attached to the object." }, + "status": { + "type": "string", + "enum": ["active", "past_due", "unpaid", "canceled", "incomplete", "incomplete_expired", "trialing", "paused"], + "description": "The status of the subscription." + }, + "test_clock": { "type": "string", "nullable": true, "description": "ID of the test clock this subscription is associated with." }, + "trial_end": { "type": "integer", "nullable": true, "description": "End of the trial period (Unix timestamp)." }, + "trial_start": { "type": "integer", "nullable": true, "description": "Start of the trial period (Unix timestamp)." } + } + }, + "SubscriptionItem": { + "type": "object", + "description": "A subscription item represents a single line item in a subscription.", + "required": ["id", "object", "created", "metadata", "price", "quantity", "subscription"], + "properties": { + "id": { "type": "string", "description": "Unique identifier for the object (e.g., `si_...`)." }, + "object": { "type": "string", "enum": ["subscription_item"], "description": "String representing the object's type." }, + "created": { "type": "integer", "description": "Time at which the object was created (Unix timestamp)." }, + "metadata": { "type": "object", "additionalProperties": { "type": "string" } }, + "price": { "$ref": "#/components/schemas/Price" }, + "quantity": { "type": "integer", "description": "The quantity of the plan to which the customer is subscribed." }, + "subscription": { "type": "string", "description": "The ID of the subscription this item belongs to." } + } + }, + "Invoice": { + "type": "object", + "description": "An invoice is a statement of amounts owed by a customer.", + "required": ["id", "object", "amount_due", "amount_paid", "created", "currency", "customer", "livemode", "metadata", "status"], + "properties": { + "id": { "type": "string", "description": "Unique identifier for the object (e.g., `in_...`).", "example": "in_abc123def456" }, + "object": { "type": "string", "enum": ["invoice"], "description": "String representing the object's type." }, + "amount_due": { "type": "integer", "description": "Final amount due at this time for the invoice, in cents." }, + "amount_paid": { "type": "integer", "description": "The amount that has been paid, in cents." }, + "amount_remaining": { "type": "integer", "description": "The amount remaining to be paid, in cents." }, + "attempt_count": { "type": "integer", "description": "Number of payment attempts made for this invoice." }, + "attempted": { "type": "boolean", "description": "Whether an attempt has been made to pay the invoice." }, + "auto_advance": { "type": "boolean", "description": "Controls whether Stripe performs automatic collection." }, + "billing_reason": { "type": "string", "nullable": true, "description": "Indicates the reason why the invoice was created.", "enum": ["subscription_create", "subscription_cycle", "subscription_update", "manual", null] }, + "collection_method": { "type": "string", "enum": ["charge_automatically", "send_invoice"], "description": "How the invoice is collected." }, + "created": { "type": "integer", "description": "Time at which the object was created (Unix timestamp)." }, + "currency": { "type": "string", "description": "Three-letter ISO currency code." }, + "customer": { "type": "string", "description": "The ID of the customer who will be billed." }, + "default_payment_method": { "type": "string", "nullable": true }, + "description": { "type": "string", "nullable": true }, + "effective_at": { "type": "integer", "nullable": true, "description": "Time at which the invoice was finalized (Unix timestamp)." }, + "hosted_invoice_url": { "type": "string", "nullable": true }, + "lines": { + "type": "object", + "description": "The individual line items that make up the invoice.", + "properties": { + "object": { "type": "string", "enum": ["list"] }, + "data": { "type": "array", "items": {} }, + "has_more": { "type": "boolean" }, + "url": { "type": "string" } + } + }, + "livemode": { "type": "boolean", "description": "Always `false` in Strimulator." }, + "metadata": { "type": "object", "additionalProperties": { "type": "string" }, "description": "Set of key-value pairs attached to the object." }, + "number": { "type": "string", "nullable": true, "description": "The invoice number (assigned upon finalization)." }, + "paid": { "type": "boolean", "description": "Whether the invoice has been paid." }, + "payment_intent": { "type": "string", "nullable": true, "description": "The PaymentIntent associated with this invoice." }, + "period_end": { "type": "integer", "description": "End of the usage period the invoice covers (Unix timestamp)." }, + "period_start": { "type": "integer", "description": "Start of the usage period the invoice covers (Unix timestamp)." }, + "status": { + "type": "string", + "enum": ["draft", "open", "paid", "uncollectible", "void"], + "description": "The status of the invoice." + }, + "subscription": { "type": "string", "nullable": true, "description": "The subscription that this invoice was prepared for." }, + "subtotal": { "type": "integer", "description": "Total of all subscriptions, invoice items, and prorations before discount or tax." }, + "total": { "type": "integer", "description": "Total after discounts and taxes." } + } + }, + "Event": { + "type": "object", + "description": "Events are Strimulator's way of letting you know when something interesting happens in your account.", + "required": ["id", "object", "api_version", "created", "data", "livemode", "pending_webhooks", "request", "type"], + "properties": { + "id": { "type": "string", "description": "Unique identifier for the object (e.g., `evt_...`).", "example": "evt_abc123def456" }, + "object": { "type": "string", "enum": ["event"], "description": "String representing the object's type." }, + "api_version": { "type": "string", "description": "The Stripe API version used to render `data`.", "example": "2024-12-18" }, + "created": { "type": "integer", "description": "Time at which the object was created (Unix timestamp)." }, + "data": { + "type": "object", + "description": "Data associated with the event.", + "required": ["object"], + "properties": { + "object": { + "type": "object", + "description": "The object that triggered the event." + }, + "previous_attributes": { + "type": "object", + "description": "The values of attributes that have changed (only included for `*.updated` events)." + } + } + }, + "livemode": { "type": "boolean", "description": "Always `false` in Strimulator." }, + "pending_webhooks": { "type": "integer", "description": "Number of webhooks that have yet to be successfully delivered." }, + "request": { + "type": "object", + "description": "Information on the API request that triggered the event.", + "properties": { + "id": { "type": "string", "nullable": true }, + "idempotency_key": { "type": "string", "nullable": true } + } + }, + "type": { "type": "string", "description": "Description of the event (e.g., `customer.created`, `payment_intent.succeeded`)." } + } + }, + "WebhookEndpoint": { + "type": "object", + "description": "A webhook endpoint receives event notifications via HTTP POST.", + "required": ["id", "object", "created", "enabled_events", "livemode", "metadata", "status", "url"], + "properties": { + "id": { "type": "string", "description": "Unique identifier for the object (e.g., `we_...`).", "example": "we_abc123def456" }, + "object": { "type": "string", "enum": ["webhook_endpoint"], "description": "String representing the object's type." }, + "api_version": { "type": "string", "nullable": true, "description": "The API version events are rendered as." }, + "application": { "type": "string", "nullable": true }, + "created": { "type": "integer", "description": "Time at which the object was created (Unix timestamp)." }, + "description": { "type": "string", "nullable": true, "description": "An optional description." }, + "enabled_events": { + "type": "array", + "items": { "type": "string" }, + "description": "The list of events to enable for this endpoint. Use `[\"*\"]` for all events." + }, + "livemode": { "type": "boolean", "description": "Always `false` in Strimulator." }, + "metadata": { "type": "object", "additionalProperties": { "type": "string" }, "description": "Set of key-value pairs attached to the object." }, + "secret": { "type": "string", "description": "The endpoint's secret, used to generate webhook signatures. Only returned on creation." }, + "status": { "type": "string", "enum": ["enabled", "disabled"], "description": "The status of the webhook endpoint." }, + "url": { "type": "string", "description": "The URL of the webhook endpoint." } + } + }, + "TestClock": { + "type": "object", + "description": "A test clock enables deterministic control over objects in testmode by simulating the passage of time.", + "required": ["id", "object", "created", "frozen_time", "livemode", "status"], + "properties": { + "id": { "type": "string", "description": "Unique identifier for the object (e.g., `clock_...`).", "example": "clock_abc123def456" }, + "object": { "type": "string", "enum": ["test_helpers.test_clock"], "description": "String representing the object's type." }, + "created": { "type": "integer", "description": "Time at which the object was created (Unix timestamp)." }, + "deletes_after": { "type": "integer", "description": "Time at which this test clock will be automatically deleted (Unix timestamp)." }, + "frozen_time": { "type": "integer", "description": "The frozen time for this test clock (Unix timestamp)." }, + "livemode": { "type": "boolean", "description": "Always `false` in Strimulator." }, + "name": { "type": "string", "nullable": true, "description": "A human-readable name for the test clock." }, + "status": { "type": "string", "enum": ["ready", "advancing", "internal_failure"], "description": "The status of the test clock." } + } + } + } + } +} diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 0000000..0485fdd --- /dev/null +++ b/docs/package.json @@ -0,0 +1,26 @@ +{ + "name": "strimulator-docs", + "private": true, + "scripts": { + "dev": "next dev --turbopack", + "build": "next build --turbopack", + "start": "next start", + "generate": "bun ./scripts/generate-docs.ts" + }, + "dependencies": { + "fumadocs-core": "^15.8.5", + "fumadocs-mdx": "^12.0.3", + "fumadocs-openapi": "^6.3.0", + "fumadocs-ui": "^15.8.5", + "next": "^15", + "react": "^19", + "react-dom": "^19" + }, + "devDependencies": { + "@types/react": "^19", + "@types/react-dom": "^19", + "typescript": "^5", + "@tailwindcss/postcss": "^4", + "tailwindcss": "^4" + } +} diff --git a/docs/postcss.config.mjs b/docs/postcss.config.mjs new file mode 100644 index 0000000..61e3684 --- /dev/null +++ b/docs/postcss.config.mjs @@ -0,0 +1,7 @@ +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/docs/scripts/generate-docs.ts b/docs/scripts/generate-docs.ts new file mode 100644 index 0000000..0b638d8 --- /dev/null +++ b/docs/scripts/generate-docs.ts @@ -0,0 +1,7 @@ +import { generateFiles } from 'fumadocs-openapi'; + +void generateFiles({ + input: ['./openapi.json'], + output: './content/docs/api', + includeDescription: true, +}); diff --git a/docs/source.config.ts b/docs/source.config.ts new file mode 100644 index 0000000..0b6ee4d --- /dev/null +++ b/docs/source.config.ts @@ -0,0 +1,7 @@ +import { defineDocs, defineConfig } from 'fumadocs-mdx/config'; + +export const docs = defineDocs({ + dir: 'content/docs', +}); + +export default defineConfig(); diff --git a/docs/superpowers/plans/2026-04-10-fumadocs-documentation.md b/docs/superpowers/plans/2026-04-10-fumadocs-documentation.md new file mode 100644 index 0000000..ad60785 --- /dev/null +++ b/docs/superpowers/plans/2026-04-10-fumadocs-documentation.md @@ -0,0 +1,1360 @@ +# Fumadocs Documentation Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add a beautiful, auto-generated documentation site to Strimulator using Fumadocs with OpenAPI-powered API reference. + +**Architecture:** A Next.js app in `docs/` using Fumadocs UI for the documentation framework. An OpenAPI 3.1 spec is hand-maintained in `docs/openapi.json`, and `fumadocs-openapi` generates interactive API reference MDX pages from it. Hand-written MDX pages cover Getting Started, Guides, and Architecture sections. + +**Tech Stack:** Next.js, Fumadocs (fumadocs-ui, fumadocs-core, fumadocs-mdx, fumadocs-openapi), Tailwind CSS, Bun + +--- + +### Task 1: Scaffold Fumadocs Next.js app + +**Files:** +- Create: `docs/package.json` +- Create: `docs/next.config.mjs` +- Create: `docs/source.config.ts` +- Create: `docs/tsconfig.json` +- Create: `docs/postcss.config.mjs` +- Create: `docs/tailwind.config.ts` (if needed by fumadocs-ui) + +- [ ] **Step 1: Create `docs/package.json`** + +```json +{ + "name": "strimulator-docs", + "private": true, + "scripts": { + "dev": "next dev --turbopack", + "build": "next build", + "start": "next start", + "generate": "bun ./scripts/generate-docs.ts" + }, + "dependencies": { + "fumadocs-core": "latest", + "fumadocs-mdx": "latest", + "fumadocs-openapi": "latest", + "fumadocs-ui": "latest", + "next": "^15", + "react": "^19", + "react-dom": "^19" + }, + "devDependencies": { + "@types/react": "^19", + "@types/react-dom": "^19", + "typescript": "^5", + "@tailwindcss/postcss": "^4", + "tailwindcss": "^4" + } +} +``` + +- [ ] **Step 2: Create `docs/next.config.mjs`** + +```js +import { createMDX } from 'fumadocs-mdx/next'; + +const withMDX = createMDX(); + +/** @type {import('next').NextConfig} */ +const config = {}; + +export default withMDX(config); +``` + +- [ ] **Step 3: Create `docs/source.config.ts`** + +```ts +import { defineDocs, defineConfig } from 'fumadocs-mdx/config'; + +export const docs = defineDocs({ + dir: 'content/docs', +}); + +export default defineConfig(); +``` + +- [ ] **Step 4: Create `docs/tsconfig.json`** + +```json +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "ESNext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [{ "name": "next" }], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".source/**/*.ts"], + "exclude": ["node_modules"] +} +``` + +- [ ] **Step 5: Create `docs/postcss.config.mjs`** + +```js +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; +``` + +- [ ] **Step 6: Install dependencies** + +Run: `cd docs && bun install` +Expected: `node_modules/` created, lock file generated + +- [ ] **Step 7: Commit** + +```bash +git add docs/package.json docs/next.config.mjs docs/source.config.ts docs/tsconfig.json docs/postcss.config.mjs docs/bun.lock +git commit -m "Scaffold Fumadocs Next.js app in docs/" +``` + +--- + +### Task 2: Set up Next.js app directory with Fumadocs UI + +**Files:** +- Create: `docs/app/layout.tsx` +- Create: `docs/app/global.css` +- Create: `docs/app/docs/layout.tsx` +- Create: `docs/app/docs/[[...slug]]/page.tsx` +- Create: `docs/lib/source.ts` +- Create: `docs/lib/layout.shared.tsx` +- Create: `docs/components/mdx.tsx` + +- [ ] **Step 1: Create `docs/app/global.css`** + +```css +@import "tailwindcss"; +@import "fumadocs-ui/css/ui.css"; +``` + +- [ ] **Step 2: Create `docs/lib/source.ts`** + +```ts +import { docs, meta } from '@/.source'; +import { createMDXSource } from 'fumadocs-mdx/runtime/next'; +import { loader } from 'fumadocs-core/source'; + +export const source = loader({ + baseUrl: '/docs', + source: createMDXSource(docs, meta), +}); +``` + +- [ ] **Step 3: Create `docs/lib/layout.shared.tsx`** + +```tsx +import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared'; + +export function baseOptions(): BaseLayoutProps { + return { + nav: { + title: 'Strimulator', + }, + links: [ + { + text: 'Documentation', + url: '/docs', + active: 'nested-url', + }, + ], + githubUrl: 'https://github.com/codeforge-tech/strimulator', + }; +} +``` + +- [ ] **Step 4: Create `docs/app/layout.tsx`** + +```tsx +import { RootProvider } from 'fumadocs-ui/provider/next'; +import type { ReactNode } from 'react'; +import './global.css'; + +export default function Layout({ children }: { children: ReactNode }) { + return ( + + + {children} + + + ); +} +``` + +- [ ] **Step 5: Create `docs/app/docs/layout.tsx`** + +```tsx +import { DocsLayout } from 'fumadocs-ui/layouts/docs'; +import { baseOptions } from '@/lib/layout.shared'; +import { source } from '@/lib/source'; +import type { ReactNode } from 'react'; + +export default function Layout({ children }: { children: ReactNode }) { + return ( + + {children} + + ); +} +``` + +- [ ] **Step 6: Create `docs/components/mdx.tsx`** + +```tsx +import defaultMdxComponents from 'fumadocs-ui/mdx'; +import type { MDXComponents } from 'mdx/types'; + +export function getMDXComponents(): MDXComponents { + return { + ...defaultMdxComponents, + }; +} +``` + +- [ ] **Step 7: Create `docs/app/docs/[[...slug]]/page.tsx`** + +```tsx +import { source } from '@/lib/source'; +import { notFound } from 'next/navigation'; +import { + DocsPage, + DocsBody, + DocsTitle, + DocsDescription, +} from 'fumadocs-ui/layouts/docs/page'; +import { getMDXComponents } from '@/components/mdx'; + +export default async function Page({ + params, +}: { + params: Promise<{ slug?: string[] }>; +}) { + const { slug } = await params; + const page = source.getPage(slug); + + if (!page) notFound(); + + const Mdx = page.data.body; + + return ( + + {page.data.title} + {page.data.description} + + + + + ); +} + +export function generateStaticParams() { + return source.generateParams(); +} +``` + +- [ ] **Step 8: Create `docs/app/page.tsx`** (root redirect to /docs) + +```tsx +import { redirect } from 'next/navigation'; + +export default function Home() { + redirect('/docs'); +} +``` + +- [ ] **Step 9: Commit** + +```bash +git add docs/app/ docs/lib/ docs/components/ +git commit -m "Set up Fumadocs UI app directory with layouts and page renderer" +``` + +--- + +### Task 3: Create initial content pages — Getting Started + +**Files:** +- Create: `docs/content/docs/index.mdx` +- Create: `docs/content/docs/meta.json` +- Create: `docs/content/docs/getting-started/meta.json` +- Create: `docs/content/docs/getting-started/installation.mdx` +- Create: `docs/content/docs/getting-started/quick-start.mdx` +- Create: `docs/content/docs/getting-started/connecting-sdk.mdx` + +- [ ] **Step 1: Create `docs/content/docs/meta.json`** + +This controls the top-level sidebar navigation order. + +```json +{ + "title": "Strimulator", + "pages": [ + "---Getting Started---", + "getting-started", + "---Guides---", + "guides", + "---API Reference---", + "api", + "---Architecture---", + "architecture" + ] +} +``` + +- [ ] **Step 2: Create `docs/content/docs/index.mdx`** + +```mdx +--- +title: Strimulator +description: A local Stripe emulator for development and testing +--- + +Strimulator is a drop-in local replacement for the Stripe API. It runs as a single process, stores everything in SQLite, and is compatible with the official `stripe` Node SDK. Use it to develop and test payment flows entirely offline — no Stripe account or network access required. + +**Think of it as [LocalStack](https://github.com/localstack/localstack), but for Stripe.** + +## Why Strimulator? + +- **Offline development** — No internet, no Stripe test mode, no rate limits +- **Fast feedback** — Instant responses, no network latency +- **Full control** — Trigger payment failures, advance subscriptions, simulate edge cases from the dashboard +- **SDK-compatible** — Point the official `stripe` package at localhost and it just works +- **Docker-ready** — Drop it into your docker-compose alongside Postgres, Redis, Firebase emulator, etc. + +## Supported Resources + +| Resource | Endpoints | State Machine | +|----------|-----------|:------------:| +| Customers | CRUD + list + search | | +| Products | CRUD + list + search | | +| Prices | create, retrieve, update, list | | +| Payment Methods | create, retrieve, attach, detach, list | | +| Payment Intents | create, retrieve, confirm, capture, cancel, list, search | ✓ | +| Setup Intents | create, retrieve, confirm, cancel, list | ✓ | +| Charges | retrieve, list | | +| Refunds | create, retrieve, list | | +| Subscriptions | create, retrieve, cancel, list, search | ✓ | +| Invoices | create, retrieve, finalize, pay, void, list, search | ✓ | +| Events | retrieve, list | | +| Webhook Endpoints | CRUD + list | | +| Test Clocks | create, retrieve, advance, delete, list | | + +## Additional Features + +- **Webhook delivery** with `Stripe-Signature` HMAC-SHA256 headers and retry logic +- **Search API** with Stripe's query language +- **expand[]** for one-level and nested expansion +- **Idempotency-Key** support for POST requests +- **Magic test tokens** — `tok_visa`, `tok_mastercard`, `tok_amex`, `tok_visa_debit` +``` + +- [ ] **Step 3: Create `docs/content/docs/getting-started/meta.json`** + +```json +{ + "title": "Getting Started", + "pages": ["installation", "quick-start", "connecting-sdk"] +} +``` + +- [ ] **Step 4: Create `docs/content/docs/getting-started/installation.mdx`** + +```mdx +--- +title: Installation +description: Install and run Strimulator locally or with Docker +--- + +## Prerequisites + +- [Bun](https://bun.sh) v1.0+ (for local install) +- Or [Docker](https://www.docker.com/) (for containerized setup) + +## Local Install + +```bash +git clone https://github.com/codeforge-tech/strimulator.git +cd strimulator +bun install +bun run dev +``` + +Strimulator is now running: + +- **API:** http://localhost:12111/v1/ +- **Dashboard:** http://localhost:12111/dashboard + +## Docker + +### Docker Compose (recommended) + +Add to your project's `docker-compose.yml`: + +```yaml +services: + strimulator: + image: ghcr.io/codeforge-tech/strimulator:latest + ports: + - "12111:12111" + volumes: + - strimulator-data:/data + +volumes: + strimulator-data: +``` + +### Build locally + +```bash +docker build -t strimulator . +docker run -p 12111:12111 strimulator +``` + +## Configuration + +| Environment Variable | Default | Description | +|---------------------|---------|-------------| +| `STRIMULATOR_PORT` | `12111` | Server port | +| `STRIMULATOR_DB_PATH` | `:memory:` | SQLite path. Use a file path for persistence across restarts | +| `STRIMULATOR_LOG_LEVEL` | `info` | Log verbosity | +| `STRIMULATOR_API_VERSION` | `2024-12-18` | Stripe API version in responses | +``` + +- [ ] **Step 5: Create `docs/content/docs/getting-started/quick-start.mdx`** + +```mdx +--- +title: Quick Start +description: Make your first API calls to Strimulator +--- + +With Strimulator running (`bun run dev`), try these requests: + +## Create a Customer + +```bash +curl -X POST http://localhost:12111/v1/customers \ + -H "Authorization: Bearer sk_test_123" \ + -d "email=hello@example.com" \ + -d "name=Jane Doe" +``` + +## Create a Product and Price + +```bash +curl -X POST http://localhost:12111/v1/products \ + -H "Authorization: Bearer sk_test_123" \ + -d "name=Pro Plan" + +# Use the product ID from the response +curl -X POST http://localhost:12111/v1/prices \ + -H "Authorization: Bearer sk_test_123" \ + -d "product=prod_xxxxx" \ + -d "unit_amount=2000" \ + -d "currency=usd" \ + -d "recurring[interval]=month" +``` + +## Create a Payment Intent + +```bash +curl -X POST http://localhost:12111/v1/payment_intents \ + -H "Authorization: Bearer sk_test_123" \ + -d "amount=2000" \ + -d "currency=usd" \ + -d "payment_method=tok_visa" \ + -d "confirm=true" +``` + +## List Resources + +```bash +curl http://localhost:12111/v1/customers \ + -H "Authorization: Bearer sk_test_123" +``` + +## Authentication + +All `/v1/` requests require a bearer token starting with `sk_test_`: + +``` +Authorization: Bearer sk_test_anything +``` + +The actual key value doesn't matter — Strimulator accepts any `sk_test_*` key. + +## Request Format + +Like real Stripe, requests use `application/x-www-form-urlencoded` (not JSON): + +```bash +curl -X POST http://localhost:12111/v1/customers \ + -H "Authorization: Bearer sk_test_123" \ + -d "email=test@example.com" \ + -d "metadata[plan]=pro" +``` +``` + +- [ ] **Step 6: Create `docs/content/docs/getting-started/connecting-sdk.mdx`** + +```mdx +--- +title: Connecting the Stripe SDK +description: Use the official Stripe SDK with Strimulator +--- + +Point the official Stripe SDK at Strimulator — no code changes needed beyond the configuration. + +## Node.js / TypeScript + +```typescript +import Stripe from "stripe"; + +const stripe = new Stripe("sk_test_strimulator", { + host: "localhost", + port: 12111, + protocol: "http", +}); + +// Use exactly like real Stripe +const customer = await stripe.customers.create({ + email: "dev@example.com", +}); + +const product = await stripe.products.create({ + name: "Pro Plan", +}); + +const price = await stripe.prices.create({ + product: product.id, + unit_amount: 2000, + currency: "usd", + recurring: { interval: "month" }, +}); + +const subscription = await stripe.subscriptions.create({ + customer: customer.id, + items: [{ price: price.id }], +}); + +console.log(subscription.status); // "active" +``` + +## Environment Variable Approach + +If your app reads `STRIPE_SECRET_KEY`, you can switch to Strimulator without touching code: + +```bash +STRIPE_SECRET_KEY=sk_test_strimulator \ +STRIPE_API_BASE=http://localhost:12111 \ + npm run dev +``` + +## Docker Compose + +When using Docker Compose, point your app at the service name: + +```yaml +services: + strimulator: + image: ghcr.io/codeforge-tech/strimulator:latest + ports: + - "12111:12111" + + your-app: + build: . + environment: + STRIPE_SECRET_KEY: sk_test_strimulator + STRIPE_API_BASE: http://strimulator:12111 + depends_on: + - strimulator +``` +``` + +- [ ] **Step 7: Verify the docs build** + +Run: `cd docs && bun run build` +Expected: Build succeeds with the index page and three getting-started pages + +- [ ] **Step 8: Commit** + +```bash +git add docs/content/docs/ +git commit -m "Add Getting Started documentation pages" +``` + +--- + +### Task 4: Create Guide pages + +**Files:** +- Create: `docs/content/docs/guides/meta.json` +- Create: `docs/content/docs/guides/webhooks.mdx` +- Create: `docs/content/docs/guides/test-clocks.mdx` +- Create: `docs/content/docs/guides/3ds-simulation.mdx` +- Create: `docs/content/docs/guides/dashboard.mdx` +- Create: `docs/content/docs/guides/idempotency.mdx` + +- [ ] **Step 1: Create `docs/content/docs/guides/meta.json`** + +```json +{ + "title": "Guides", + "pages": ["webhooks", "test-clocks", "3ds-simulation", "dashboard", "idempotency"] +} +``` + +- [ ] **Step 2: Create `docs/content/docs/guides/webhooks.mdx`** + +```mdx +--- +title: Webhooks +description: Register webhook endpoints and receive event notifications +--- + +Strimulator delivers webhook events just like Stripe — with HMAC-SHA256 signatures, retry logic, and the same event shapes. + +## Registering a Webhook Endpoint + +```bash +curl -X POST http://localhost:12111/v1/webhook_endpoints \ + -H "Authorization: Bearer sk_test_123" \ + -d "url=http://localhost:3000/webhooks/stripe" \ + -d "enabled_events[]=customer.created" \ + -d "enabled_events[]=payment_intent.succeeded" +``` + +Or using the SDK: + +```typescript +const endpoint = await stripe.webhookEndpoints.create({ + url: "http://localhost:3000/webhooks/stripe", + enabled_events: [ + "customer.created", + "payment_intent.succeeded", + ], +}); +``` + +Use `enabled_events: ["*"]` to receive all event types. + +## Signature Verification + +Strimulator signs webhook payloads with HMAC-SHA256, compatible with `stripe.webhooks.constructEvent()`: + +```typescript +import Stripe from "stripe"; + +const stripe = new Stripe("sk_test_strimulator", { + host: "localhost", + port: 12111, + protocol: "http", +}); + +// In your webhook handler +const event = stripe.webhooks.constructEvent( + body, + request.headers["stripe-signature"], + endpoint.secret, // from the webhook endpoint's `secret` field +); +``` + +## Retry Behavior + +When your endpoint returns a non-2xx status, Strimulator retries delivery. Failed deliveries and their status are visible in the dashboard. + +## Managing Webhooks from the Dashboard + +Open http://localhost:12111/dashboard and go to the **Webhooks** tab to: + +- View registered webhook endpoints +- See delivery history and status +- Retry failed deliveries +``` + +- [ ] **Step 3: Create `docs/content/docs/guides/test-clocks.mdx`** + +```mdx +--- +title: Test Clocks +description: Simulate time advancement for subscription billing +--- + +Test clocks let you simulate the passage of time to test subscription lifecycle events — billing cycles, trial expirations, and period transitions. + +## Creating a Test Clock + +```typescript +const clock = await stripe.testHelpers.testClocks.create({ + frozen_time: Math.floor(Date.now() / 1000), + name: "Billing test", +}); +``` + +## Linking a Subscription to a Clock + +Create a customer with `test_clock`, then create subscriptions on that customer: + +```typescript +const customer = await stripe.customers.create({ + email: "test@example.com", + test_clock: clock.id, +}); + +const subscription = await stripe.subscriptions.create({ + customer: customer.id, + items: [{ price: priceId }], +}); +``` + +## Advancing Time + +Move the clock forward to trigger billing events: + +```typescript +await stripe.testHelpers.testClocks.advance(clock.id, { + frozen_time: clock.frozen_time + 30 * 24 * 60 * 60, // +30 days +}); +``` + +When advanced, Strimulator processes billing cycles for linked subscriptions: + +- Rolls subscription periods forward +- Creates and finalizes invoices +- Processes payments (creates charges) +- Handles trial-to-active transitions + +## Dashboard + +You can also advance test clocks from the dashboard's **Actions** panel without writing code. +``` + +- [ ] **Step 4: Create `docs/content/docs/guides/3ds-simulation.mdx`** + +```mdx +--- +title: 3D Secure Simulation +description: Simulate 3DS authentication challenges in payment flows +--- + +Strimulator supports simulating 3D Secure (3DS) authentication challenges for testing payment flows that require additional verification. + +## Triggering 3DS + +Use the magic token `tok_threeDSecureRequired` (a payment method with last4 `3220`) to trigger `requires_action` status on confirm: + +```typescript +const paymentIntent = await stripe.paymentIntents.create({ + amount: 2000, + currency: "usd", + payment_method: "tok_threeDSecureRequired", + confirm: true, +}); + +console.log(paymentIntent.status); // "requires_action" +console.log(paymentIntent.next_action?.type); // "use_stripe_sdk" +``` + +## Completing the 3DS Challenge + +Re-confirm the payment intent to simulate the user completing the 3DS challenge: + +```typescript +const completed = await stripe.paymentIntents.confirm(paymentIntent.id); +console.log(completed.status); // "succeeded" +``` + +## Flow Summary + +``` +Create PI with tok_threeDSecureRequired + confirm: true + → status: "requires_action" + → next_action: { type: "use_stripe_sdk" } + +Re-confirm the PI + → status: "succeeded" + → charge created +``` +``` + +- [ ] **Step 5: Create `docs/content/docs/guides/dashboard.mdx`** + +```mdx +--- +title: Dashboard +description: Real-time debug interface for monitoring and testing +--- + +Strimulator includes a built-in debug dashboard at http://localhost:12111/dashboard. + +## Activity Feed + +Live stream of all API requests showing method, path, status code, and timing. Uses Server-Sent Events (SSE) for real-time updates. + +## Resource Explorer + +Browse all stored objects by type. Click any object to view its full JSON representation. + +## Actions Panel + +Trigger simulated scenarios without writing code: + +| Action | Description | +|--------|-------------| +| **Fail Next Payment** | Force the next PaymentIntent confirmation to fail (`card_declined`, `insufficient_funds`, `expired_card`) | +| **Advance Test Clock** | Move a test clock forward in time, triggering subscription transitions | +| **Retry Webhook** | Re-deliver an event to a webhook endpoint | +| **Expire Payment Intent** | Force a PaymentIntent into canceled state | +| **Cycle Subscription** | Advance a subscription to the next billing period | + +## Webhooks Tab + +View registered webhook endpoints, delivery history, and retry failed deliveries. + +## Access + +The dashboard is not auth-protected — it's designed for local development use only. +``` + +- [ ] **Step 6: Create `docs/content/docs/guides/idempotency.mdx`** + +```mdx +--- +title: Idempotency +description: How Idempotency-Key works in Strimulator +--- + +Strimulator supports the `Idempotency-Key` header on POST requests, matching Stripe's behavior. + +## How It Works + +Send an `Idempotency-Key` header with any POST request. If the same key is sent again, Strimulator returns the cached response instead of creating a duplicate resource. + +```bash +curl -X POST http://localhost:12111/v1/customers \ + -H "Authorization: Bearer sk_test_123" \ + -H "Idempotency-Key: unique-key-123" \ + -d "email=test@example.com" + +# Same key → same response, no duplicate customer +curl -X POST http://localhost:12111/v1/customers \ + -H "Authorization: Bearer sk_test_123" \ + -H "Idempotency-Key: unique-key-123" \ + -d "email=test@example.com" +``` + +## SDK Usage + +The Stripe SDK automatically generates idempotency keys for most create operations. You can also specify one explicitly: + +```typescript +const customer = await stripe.customers.create( + { email: "test@example.com" }, + { idempotencyKey: "unique-key-123" }, +); +``` + +## Behavior + +- Keys are scoped to the API key and endpoint +- Cached responses are returned with the same status code and body +- Only POST requests support idempotency keys +- Keys are stored in-memory (or SQLite depending on `STRIMULATOR_DB_PATH`) +``` + +- [ ] **Step 7: Verify the docs build** + +Run: `cd docs && bun run build` +Expected: Build succeeds with all guide pages in the sidebar + +- [ ] **Step 8: Commit** + +```bash +git add docs/content/docs/guides/ +git commit -m "Add documentation guide pages" +``` + +--- + +### Task 5: Create Architecture / Contributing pages + +**Files:** +- Create: `docs/content/docs/architecture/meta.json` +- Create: `docs/content/docs/architecture/overview.mdx` +- Create: `docs/content/docs/architecture/request-lifecycle.mdx` +- Create: `docs/content/docs/architecture/services.mdx` +- Create: `docs/content/docs/architecture/contributing.mdx` + +- [ ] **Step 1: Create `docs/content/docs/architecture/meta.json`** + +```json +{ + "title": "Architecture", + "pages": ["overview", "request-lifecycle", "services", "contributing"] +} +``` + +- [ ] **Step 2: Create `docs/content/docs/architecture/overview.mdx`** + +```mdx +--- +title: Architecture Overview +description: How Strimulator is built +--- + +Strimulator emulates Stripe's REST API over HTTP using Elysia (Bun's web framework) + SQLite via Drizzle ORM. It returns real `Stripe.*` types from the official `stripe` npm package. + +## Tech Stack + +| Component | Technology | +|-----------|-----------| +| Runtime | [Bun](https://bun.sh) | +| Framework | [ElysiaJS](https://elysiajs.com) | +| Database | SQLite via [Drizzle ORM](https://orm.drizzle.team) + bun:sqlite | +| Types | Imported from [`stripe`](https://www.npmjs.com/package/stripe) npm package | +| Dashboard | [Preact](https://preactjs.com) + [HTM](https://github.com/developit/htm) | +| Testing | bun:test | + +## Project Structure + +``` +src/ + routes/ # ElysiaJS route plugins (one per Stripe resource) + services/ # Business logic and state machines + db/schema/ # Drizzle ORM table definitions + middleware/ # Auth, idempotency, form parsing, request logging + dashboard/ # Debug dashboard (API + Preact SPA) + lib/ # Shared utilities (IDs, pagination, expand, search) + errors/ # Stripe-compatible error factory +tests/ + unit/ # Service-layer tests + integration/ # HTTP request/response tests + sdk/ # Tests using the official stripe npm package +``` + +## Key Design Decisions + +- **Stripe type fidelity:** Services return `Stripe.*` types directly from the official package, ensuring response shapes always match real Stripe. +- **Synchronous DB:** bun:sqlite is synchronous, so services are synchronous. Routes are async only for body parsing and expansion. +- **In-memory by default:** Using `:memory:` SQLite for ephemeral storage makes tests fast and isolated. File-backed persistence is opt-in. +- **Single process:** No external dependencies — one binary, one port, everything included. +``` + +- [ ] **Step 3: Create `docs/content/docs/architecture/request-lifecycle.mdx`** + +```mdx +--- +title: Request Lifecycle +description: How an HTTP request flows through Strimulator +--- + +Every API request passes through a middleware chain before reaching the route handler: + +``` +HTTP request + → apiKeyAuth (validates Bearer sk_test_*) + → idempotencyMiddleware (caches POST responses by Idempotency-Key header) + → requestLogger (emits to globalBus for dashboard SSE) + → route handler + → parseStripeBody() (decodes x-www-form-urlencoded with bracket notation) + → service method (DB read/write, state validation) + → eventService.emit() (triggers webhook delivery) + → response +``` + +## Middleware + +### API Key Auth + +Validates the `Authorization: Bearer sk_test_*` header. Any key starting with `sk_test_` is accepted — Strimulator doesn't verify specific keys. + +Source: `src/middleware/api-key-auth.ts` + +### Idempotency + +Caches POST responses keyed by the `Idempotency-Key` header. Replay requests return the cached response without re-executing the handler. + +Source: `src/middleware/idempotency.ts` + +### Request Logger + +Emits every request to the global event bus for the dashboard's live activity feed via SSE. + +Source: `src/middleware/request-logger.ts` + +### Form Parser + +Decodes `application/x-www-form-urlencoded` bodies with Stripe's bracket notation (`metadata[key]=value`, `items[0][price]=...`). + +Source: `src/middleware/form-parser.ts` + +## Route → Service → Response + +Routes are Elysia plugin factories that: + +1. Parse the form-encoded body with `parseStripeBody()` +2. Call the appropriate service method +3. Emit events via `eventService.emit()` (triggers webhook delivery) +4. Return the Stripe-typed response + +## Expansion + +Routes that support `?expand[]=field` use `applyExpand()` with a config mapping field names to resolver functions. Supports nested expansion via dot notation (`expand[]=latest_invoice.payment_intent`). + +Source: `src/lib/expand.ts` + +## Pagination + +All list endpoints use cursor-based pagination via `created` timestamp and `starting_after` parameter. Returns `{ object: "list", data: [...], has_more: boolean }`. + +Source: `src/lib/pagination.ts` +``` + +- [ ] **Step 4: Create `docs/content/docs/architecture/services.mdx`** + +```mdx +--- +title: Services +description: Business logic layer and state machines +--- + +Services are classes in `src/services/` that encapsulate all business logic. Each service takes `StrimulatorDB` in the constructor and returns `Stripe.*` types. + +## Pattern + +```typescript +class CustomerService { + constructor(private db: StrimulatorDB) {} + + create(params: CreateParams): Stripe.Customer { + // Validate, insert to DB, build shape, return + } + + retrieve(id: string): Stripe.Customer { + // Query DB, build shape, return (or throw 404) + } +} +``` + +Each resource has a `build*Shape()` function that constructs the full Stripe object. The full JSON is stored in a `data` text column; key fields are indexed separately for queries. + +## State Machines + +Resources with lifecycle states validate transitions with `stateTransitionError()`: + +### Payment Intents + +``` +requires_payment_method → requires_confirmation → requires_action → succeeded + → canceled +``` + +### Setup Intents + +``` +requires_payment_method → requires_confirmation → succeeded + → canceled +``` + +### Subscriptions + +``` +trialing → active → past_due → canceled + → canceled +``` + +### Invoices + +``` +draft → open → paid + → void +``` + +## Event Emission + +Services with state changes emit events via an optional `EventService` dependency: + +```typescript +this.eventService?.emit("payment_intent.succeeded", paymentIntent); +``` + +`EventService.emit()` persists the event to DB and synchronously notifies listeners. `WebhookDeliveryService` is registered as a listener and delivers to matching webhook endpoints with HMAC-SHA256 signatures. + +## Search + +Search endpoints load all rows and filter in-memory via `parseSearchQuery()` / `matchesCondition()`. Supports Stripe's query language: + +- `email:"foo@bar.com"` — exact match +- `status~"act"` — contains +- `metadata["key"]:"value"` — metadata search +- Combine with `AND` +``` + +- [ ] **Step 5: Create `docs/content/docs/architecture/contributing.mdx`** + +```mdx +--- +title: Contributing +description: Development setup and guidelines for contributors +--- + +## Development Setup + +```bash +git clone https://github.com/codeforge-tech/strimulator.git +cd strimulator +bun install +bun run dev # Start with watch mode +``` + +## Running Tests + +```bash +bun test # Run all tests (unit + integration + SDK) +bun test tests/unit/ # Run unit tests only +bun test tests/sdk/ # Run SDK tests only +bun test tests/integration/customers.test.ts # Single file +bun x tsc --noEmit # Type check +``` + +## Adding a New Resource + +1. **Schema:** Create `src/db/schema/.ts` with the Drizzle table definition +2. **Service:** Create `src/services/.ts` with a class that takes `StrimulatorDB` +3. **Routes:** Create `src/routes/.ts` as an Elysia plugin factory +4. **Wire up:** Register routes in `src/app.ts` +5. **Tests:** Add unit tests in `tests/unit/` and SDK tests in `tests/sdk/` + +## Conventions + +- **IDs:** Generated via `generateId(type)` with crypto.randomBytes. Each type has a prefix (`cus_`, `pi_`, `sub_`, etc.) defined in `src/lib/id-generator.ts`. +- **Body format:** Always `application/x-www-form-urlencoded` with bracket notation. Use `parseStripeBody()`. +- **Soft deletes:** Use a `deleted` integer flag (0/1), never hard-delete. +- **No conventional commits:** Write clear descriptions, don't prefix with `feat:`, `fix:`, etc. + +## Database Migrations + +```bash +bun run db:generate # Generate migration from schema changes +bun run db:migrate # Apply migrations +``` +``` + +- [ ] **Step 6: Verify the docs build** + +Run: `cd docs && bun run build` +Expected: Build succeeds with all architecture pages in the sidebar + +- [ ] **Step 7: Commit** + +```bash +git add docs/content/docs/architecture/ +git commit -m "Add architecture and contributing documentation" +``` + +--- + +### Task 6: Create OpenAPI spec + +**Files:** +- Create: `docs/openapi.json` + +- [ ] **Step 1: Create `docs/openapi.json`** + +Write a complete OpenAPI 3.1 spec covering all 13 resources with their endpoints. The spec must include: + +- `info` with title "Strimulator API", version "1.0.0" +- `servers` pointing to `http://localhost:12111` +- `security` with bearer auth (`sk_test_*`) +- All paths from the route analysis: + - **Customers:** POST/GET/DELETE `/v1/customers`, GET `/v1/customers/search`, GET/POST/DELETE `/v1/customers/{id}` + - **Products:** POST/GET `/v1/products`, GET/POST/DELETE `/v1/products/{id}` + - **Prices:** POST/GET `/v1/prices`, GET/POST `/v1/prices/{id}` + - **Payment Methods:** POST/GET `/v1/payment_methods`, GET `/v1/payment_methods/{id}`, POST attach/detach + - **Payment Intents:** POST/GET `/v1/payment_intents`, GET search, GET/POST `/{id}`, POST confirm/capture/cancel + - **Setup Intents:** POST/GET `/v1/setup_intents`, GET `/{id}`, POST confirm/cancel + - **Charges:** GET `/v1/charges`, GET `/{id}` + - **Refunds:** POST/GET `/v1/refunds`, GET `/{id}` + - **Subscriptions:** POST/GET `/v1/subscriptions`, GET search, GET/POST `/{id}`, DELETE `/{id}` + - **Invoices:** POST/GET `/v1/invoices`, GET search, GET `/{id}`, POST finalize/pay/void + - **Events:** GET `/v1/events`, GET `/{id}` + - **Webhook Endpoints:** POST/GET `/v1/webhook_endpoints`, GET/DELETE `/{id}` + - **Test Clocks:** POST/GET `/v1/test_helpers/test_clocks`, GET/DELETE `/{id}`, POST advance +- Request bodies using `application/x-www-form-urlencoded` +- Response schemas matching Stripe object shapes +- Pagination query parameters (`limit`, `starting_after`, `ending_before`) +- Search query parameters (`query`, `limit`) +- Expand query parameter (`expand[]`) +- Error response schema +- Tags grouping endpoints by resource + +The spec file will be large (~2000+ lines). Write the complete spec with all endpoints, parameters, and schemas. Each path operation needs: summary, tags, parameters, requestBody (for POST), responses (200 with schema, 400/401/404 error shapes). + +Use `$ref` for shared components: pagination params, list response wrapper, search response wrapper, error response, and common Stripe object schemas. + +- [ ] **Step 2: Validate the spec** + +Run: `cd docs && bunx @redocly/cli lint openapi.json` +Expected: No errors (warnings are acceptable) + +- [ ] **Step 3: Commit** + +```bash +git add docs/openapi.json +git commit -m "Add OpenAPI 3.1 spec for all Strimulator endpoints" +``` + +--- + +### Task 7: Set up Fumadocs OpenAPI integration and generate API docs + +**Files:** +- Create: `docs/lib/openapi.ts` +- Create: `docs/components/api-page.tsx` +- Create: `docs/components/api-page.client.tsx` +- Create: `docs/scripts/generate-docs.ts` +- Create: `docs/content/docs/api/meta.json` +- Modify: `docs/components/mdx.tsx` (add APIPage component) + +- [ ] **Step 1: Create `docs/lib/openapi.ts`** + +```ts +import { createOpenAPI } from 'fumadocs-openapi/server'; + +export const openapi = createOpenAPI({ + input: ['./openapi.json'], +}); +``` + +- [ ] **Step 2: Create `docs/components/api-page.tsx`** + +```tsx +import { openapi } from '@/lib/openapi'; +import { createAPIPage } from 'fumadocs-openapi/ui'; +import client from './api-page.client'; + +export const APIPage = createAPIPage(openapi, { + client, +}); +``` + +- [ ] **Step 3: Create `docs/components/api-page.client.tsx`** + +```tsx +'use client'; +import { defineClientConfig } from 'fumadocs-openapi/ui/client'; + +export default defineClientConfig({}); +``` + +- [ ] **Step 4: Modify `docs/components/mdx.tsx` to include APIPage** + +```tsx +import defaultMdxComponents from 'fumadocs-ui/mdx'; +import { APIPage } from '@/components/api-page'; +import type { MDXComponents } from 'mdx/types'; + +export function getMDXComponents(): MDXComponents { + return { + ...defaultMdxComponents, + APIPage, + }; +} +``` + +- [ ] **Step 5: Create `docs/content/docs/api/meta.json`** + +```json +{ + "title": "API Reference" +} +``` + +- [ ] **Step 6: Create `docs/scripts/generate-docs.ts`** + +```ts +import { generateFiles } from 'fumadocs-openapi'; +import { openapi } from '@/lib/openapi'; + +void generateFiles({ + input: openapi, + output: './content/docs/api', + includeDescription: true, +}); +``` + +- [ ] **Step 7: Generate the API docs** + +Run: `cd docs && bun run generate` +Expected: MDX files created in `docs/content/docs/api/` for each resource group + +- [ ] **Step 8: Verify the full docs build** + +Run: `cd docs && bun run build` +Expected: Build succeeds with all pages — getting started, guides, API reference, architecture + +- [ ] **Step 9: Commit** + +```bash +git add docs/lib/openapi.ts docs/components/ docs/scripts/ docs/content/docs/api/ +git commit -m "Add OpenAPI integration and auto-generated API reference" +``` + +--- + +### Task 8: Final verification and dev experience + +**Files:** +- Modify: root `README.md` (add link to docs) + +- [ ] **Step 1: Start the docs dev server and verify** + +Run: `cd docs && bun run dev` +Expected: Dev server starts, visit http://localhost:3000 — verify: +- Landing page renders +- Sidebar shows all sections (Getting Started, Guides, API Reference, Architecture) +- Navigation works between pages +- API reference pages render with interactive parameter tables +- Dark mode toggle works +- Search works + +- [ ] **Step 2: Add docs link to root README.md** + +Add a "Documentation" link to the header section of the root README.md pointing to the docs. + +- [ ] **Step 3: Add `.gitignore` for docs** + +Create `docs/.gitignore`: + +``` +.next/ +node_modules/ +.source/ +``` + +- [ ] **Step 4: Final commit** + +```bash +git add docs/.gitignore README.md +git commit -m "Add docs gitignore and link documentation from README" +``` diff --git a/docs/superpowers/specs/2026-04-10-fumadocs-documentation-design.md b/docs/superpowers/specs/2026-04-10-fumadocs-documentation-design.md new file mode 100644 index 0000000..59cf4dd --- /dev/null +++ b/docs/superpowers/specs/2026-04-10-fumadocs-documentation-design.md @@ -0,0 +1,142 @@ +# Fumadocs Documentation — Design Spec + +## Overview + +Add a beautiful, auto-generated documentation site to Strimulator using Fumadocs (Next.js). The docs live in `docs/` as a monorepo setup. API reference pages are auto-generated from an OpenAPI spec extracted from the codebase. + +## Content Areas + +### 1. Getting Started +- **Installation** — bun/npm/docker install, environment variables +- **Quick Start** — spin up Strimulator, make first API call +- **Connecting the Stripe SDK** — configure official `stripe` npm package to point at Strimulator + +### 2. API Reference (auto-generated) +All 13 resources with endpoints, parameters, response shapes, and code examples: +- Customers, Products, Prices +- Payment Intents, Payment Methods, Charges, Refunds +- Setup Intents, Subscriptions, Invoices +- Events, Webhook Endpoints, Test Clocks + +Generated from an OpenAPI spec via `fumadocs-openapi`. + +### 3. Guides +- **Webhooks** — registering endpoints, HMAC verification, retry behavior +- **Test Clocks** — creating clocks, advancing time, billing cycle simulation +- **3DS Simulation** — triggering `requires_action`, completing 3DS challenges +- **Dashboard** — activity feed, resource explorer, actions panel +- **Idempotency** — how idempotency keys work in Strimulator + +### 4. Architecture / Contributing +- **Overview** — tech stack, project structure +- **Request Lifecycle** — middleware chain, body parsing, expansion, events +- **Services** — service pattern, state machines, DB conventions +- **Contributing** — dev setup, running tests, adding new resources + +## Directory Structure + +``` +docs/ + package.json # Next.js + Fumadocs deps + next.config.mjs + source.config.ts # Fumadocs content source + tsconfig.json + content/docs/ + index.mdx # Docs landing page + meta.json # Root navigation order + getting-started/ + meta.json + installation.mdx + quick-start.mdx + connecting-sdk.mdx + guides/ + meta.json + webhooks.mdx + test-clocks.mdx + 3ds-simulation.mdx + dashboard.mdx + idempotency.mdx + api/ + meta.json + (auto-generated MDX files from OpenAPI spec) + architecture/ + meta.json + overview.mdx + request-lifecycle.mdx + services.mdx + contributing.mdx + app/ + layout.tsx # Root layout with Fumadocs provider + (docs)/ + [[...slug]]/ + page.tsx # Docs page renderer + api/ + (auto-generated API page components) + openapi.json # Generated OpenAPI spec + scripts/ + generate-openapi.ts # Builds openapi.json from route/service analysis + generate-docs.ts # Runs fumadocs-openapi to produce MDX from spec +``` + +## Technical Approach + +### OpenAPI Spec Generation + +A TypeScript script (`docs/scripts/generate-openapi.ts`) constructs an OpenAPI 3.1 spec by: +- Defining each of the 13 resources and their endpoints +- Documenting request parameters (path params, query params, form-encoded body fields) +- Documenting response schemas matching Stripe object shapes +- Including authentication requirements (Bearer `sk_test_*`) +- Documenting error response shapes + +This is a hand-maintained script that mirrors the route definitions — not runtime introspection. When routes change, the script is updated and re-run. + +### Fumadocs OpenAPI Integration + +Uses `fumadocs-openapi` package: +1. `createOpenAPI()` loads the spec in server components +2. `generateFiles()` script produces MDX files in `content/docs/api/` +3. API pages render with interactive parameter tables, request/response examples, and code snippets + +### Fumadocs UI + +- Default Fumadocs UI theme (`fumadocs-ui`) +- Project branding: title "Strimulator", description, optional logo +- Built-in full-text search +- Dark mode support +- Responsive layout +- Syntax-highlighted code blocks + +### Package Manager + +Bun — consistent with the main project. + +### Dev Workflow + +```bash +cd docs +bun install +bun run dev # Start Fumadocs dev server +bun run build # Production build +bun run generate # Regenerate API docs from OpenAPI spec +``` + +## Key Decisions + +| Decision | Choice | Rationale | +|----------|--------|-----------| +| Framework | Fumadocs (Next.js) | Best UI, OpenAPI integration, TypeScript-native | +| Location | `docs/` monorepo | Docs stay in sync with code | +| API docs | Auto-generated from OpenAPI spec | 13 resources with many endpoints — manual is unsustainable | +| OpenAPI generation | Hand-maintained script | More control than runtime introspection, simpler than decorators | +| Package manager | Bun | Consistent with main project | +| i18n | None | English only | +| Search | Built-in Fumadocs search | No external service needed | + +## Out of Scope + +- Custom domain / deployment (can be added later) +- Internationalization +- Blog section +- Versioned docs (single version for now) +- Interactive API playground with live Strimulator instance diff --git a/docs/tsconfig.json b/docs/tsconfig.json new file mode 100644 index 0000000..e2a2cc1 --- /dev/null +++ b/docs/tsconfig.json @@ -0,0 +1,41 @@ +{ + "compilerOptions": { + "target": "ESNext", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "ESNext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": [ + "./*" + ] + } + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ".source/**/*.ts", + "next-env.d.ts", + ".next/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] +}