diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..6240da8 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,21 @@ +# build output +dist/ +# generated types +.astro/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store diff --git a/docs/README.md b/docs/README.md index c937e8c..6ea8731 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,7 +1,28 @@ -# Documentation +# goway documentation site -- [design.md](design.md) — the architecture of the library and how its behavior - maps onto Flyway's. +This directory contains the documentation website for goway, built with +[Astro](https://astro.build/) and [Starlight](https://starlight.astro.build/) +using the Ion theme with a monochrome palette. -The top level [README](../README.md) covers installation, the quick start, the -migration naming convention, the available commands, and the command line tool. +## Local development + +```sh +pnpm install +pnpm dev # serves the site at http://localhost:4321/goway +``` + +## Building + +```sh +pnpm build # outputs the static site to ./dist +pnpm preview # serves the built site locally +``` + +## Layout + +- `astro.config.mjs` — site configuration, theme, and the navigation sidebar. +- `src/content/docs/` — the documentation pages, grouped into Start Here, + Guides, Reference, and Cookbook. +- `src/styles/theme.css` — the monochrome palette overrides for the Ion theme. +- `design.md` — the internal design notes for the library, kept alongside the + site sources. diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs new file mode 100644 index 0000000..791bd72 --- /dev/null +++ b/docs/astro.config.mjs @@ -0,0 +1,102 @@ +// @ts-check +import { defineConfig } from 'astro/config'; +import starlight from '@astrojs/starlight'; +import { ion } from 'starlight-ion-theme'; + +// https://astro.build/config +export default defineConfig({ + // The site is published as a GitHub Pages project site at + // https://cgardev.github.io/goway/, so the origin and the base path + // are configured separately. + site: 'https://cgardev.github.io', + base: '/goway', + integrations: [ + starlight({ + title: 'goway', + description: + 'A version-based database schema migration library for Go, modeled on Flyway, for PostgreSQL and SQLite.', + // Apply the Ion theme, recolored with the monochrome + // palette defined in ./src/styles/theme.css. useCustomECTheme:false lets + // code blocks follow the same palette instead of Ion's built-in theme. + plugins: [ion({ useCustomECTheme: false })], + customCss: ['./src/styles/theme.css'], + // The header logo and the browser tab icon both use the SQL file + // artwork. The logo is resolved relative to the project root, while the + // favicon is served from the public directory. + logo: { + src: './src/assets/sql-file.png', + alt: 'goway', + replacesTitle: false, + }, + favicon: '/sql-file.png', + social: [ + { + icon: 'github', + label: 'GitHub', + href: 'https://github.com/cgardev/goway', + }, + ], + // Surface an "Edit page" link pointing at the documentation sources + // within the repository. + editLink: { + baseUrl: 'https://github.com/cgardev/goway/edit/main/docs/', + }, + sidebar: [ + { + label: 'Start Here', + items: [ + { label: 'Introduction', link: '/' }, + { label: 'Getting Started', slug: 'getting-started' }, + ], + }, + { + label: 'Guides', + items: [ + { label: 'Writing Migrations', slug: 'guides/writing-migrations' }, + { label: 'Running Migrations', slug: 'guides/running-migrations' }, + { label: 'Configuration', slug: 'guides/configuration' }, + { label: 'Callbacks', slug: 'guides/callbacks' }, + { label: 'Dialects', slug: 'guides/dialects' }, + { label: 'Command Line', slug: 'guides/command-line' }, + ], + }, + { + label: 'Reference', + items: [ + { label: 'Overview', slug: 'reference' }, + { label: 'Configuration', slug: 'reference/configuration' }, + { label: 'Commands', slug: 'reference/commands' }, + { label: 'Migration Naming', slug: 'reference/migration-naming' }, + { label: 'Schema History', slug: 'reference/schema-history' }, + { label: 'Placeholders', slug: 'reference/placeholders' }, + { label: 'Callbacks', slug: 'reference/callbacks' }, + { label: 'Dialects', slug: 'reference/dialects' }, + { label: 'Command Line', slug: 'reference/cli' }, + ], + }, + { + label: 'Cookbook', + items: [ + { label: 'Overview', slug: 'cookbook' }, + { + label: 'Embedding Migrations', + slug: 'cookbook/embedding-migrations', + }, + { + label: 'Programmatic Setup', + slug: 'cookbook/programmatic-setup', + }, + { + label: 'Non-Transactional Migrations', + slug: 'cookbook/non-transactional-migrations', + }, + { + label: 'Baselining an Existing Database', + slug: 'cookbook/baseline-existing-database', + }, + ], + }, + ], + }), + ], +}); diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 0000000..4a243ea --- /dev/null +++ b/docs/package.json @@ -0,0 +1,25 @@ +{ + "name": "goway-docs", + "type": "module", + "version": "0.0.1", + "packageManager": "pnpm@10.24.0", + "scripts": { + "dev": "astro dev", + "start": "astro dev", + "build": "astro build", + "preview": "astro preview", + "astro": "astro" + }, + "dependencies": { + "@astrojs/starlight": "^0.39.2", + "astro": "^6.3.1", + "sharp": "^0.34.5", + "starlight-ion-theme": "^2.4.0" + }, + "pnpm": { + "onlyBuiltDependencies": [ + "esbuild", + "sharp" + ] + } +} diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml new file mode 100644 index 0000000..b8f53db --- /dev/null +++ b/docs/pnpm-lock.yaml @@ -0,0 +1,4432 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@astrojs/starlight': + specifier: ^0.39.2 + version: 0.39.2(astro@6.4.2(@types/node@24.12.4)(rollup@4.60.4)) + astro: + specifier: ^6.3.1 + version: 6.4.2(@types/node@24.12.4)(rollup@4.60.4) + sharp: + specifier: ^0.34.5 + version: 0.34.5 + starlight-ion-theme: + specifier: ^2.4.0 + version: 2.4.0(@astrojs/starlight@0.39.2(astro@6.4.2(@types/node@24.12.4)(rollup@4.60.4)))(astro@6.4.2(@types/node@24.12.4)(rollup@4.60.4)) + +packages: + + '@antfu/install-pkg@1.1.0': + resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} + + '@antfu/utils@8.1.1': + resolution: {integrity: sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==} + + '@astrojs/compiler@4.0.0': + resolution: {integrity: sha512-eouss7G8ygdZqHuke033VMcVw5HTZUu+PXd/h06DGDUg/jt5btPYPqh66ENWw/mU78rBrf/oeC4oqoBwMtDMNA==} + + '@astrojs/internal-helpers@0.10.0': + resolution: {integrity: sha512-Ry2R3VPeIN4uPCSA4xQc+e+vsJXkalKpEbDc07hV+a/o5Bs2N/s/uDcPJH/05L19DKh9tAy7e6JM3YZ6Cxfezw==} + + '@astrojs/internal-helpers@0.9.1': + resolution: {integrity: sha512-1pWuARqYom/TzuU3+0ZugsTrKlUydWKuULmDqSMTuonY+9IRDUEGKX/8PXQ1nBxRq3w85uGtd9q9SXfqEldMIQ==} + + '@astrojs/markdown-remark@7.1.2': + resolution: {integrity: sha512-caXZ4Dc2St2dW8luEg22GlP0gupLdztCTQE4EzZOxW1pqWXz9mbeJEuHUkgDYcKWW8tjIHkydYDhWLVoxJ327Q==} + + '@astrojs/markdown-remark@7.2.0': + resolution: {integrity: sha512-+YxmVQu1Bd+MFfSzjq1rOJvD9+nIOJzz5YIIhdIH01RrxRkKbyKoEgyIqP3yv51MhzMDgd79QaPv+kCVPT8vHw==} + + '@astrojs/mdx@5.0.6': + resolution: {integrity: sha512-4dKe0ZMmqujofPNDHahzClkwinn9f8jHPcaXcgdGvPAlboD2mjzkUCofli2cBnxYAkdfhC6d50gBJ8i/cH8gHw==} + engines: {node: '>=22.12.0'} + peerDependencies: + astro: ^6.0.0 + + '@astrojs/prism@4.0.2': + resolution: {integrity: sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA==} + engines: {node: '>=22.12.0'} + + '@astrojs/sitemap@3.7.3': + resolution: {integrity: sha512-f8euLVsyeAmAkSm/1M2Kb8sL8byQmfgbvBNaHFItCheTj/IpiJYSEWVcqDHZ/yEHxiS7+w87mQkzwZaPHmk5GA==} + + '@astrojs/starlight@0.39.2': + resolution: {integrity: sha512-vlw+bwnjtf5buCTUtLU7JfV6D3knslxqnspr6LKs6hfRuFZiyr5hT44F7GyDqR9FKANUqFxnIzWM81F1k/kOUA==} + peerDependencies: + astro: ^6.0.0 + + '@astrojs/telemetry@3.3.2': + resolution: {integrity: sha512-j8DNruA8ors99Al39RYZPJK4DC1bKkoNm93mAMuBhY9TCNC4R8n1q7ovFnJ5qhGh5Lsh7pa1gpQVpYpsJPeTHQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + + '@capsizecss/unpack@4.0.0': + resolution: {integrity: sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==} + engines: {node: '>=18'} + + '@clack/core@1.4.0': + resolution: {integrity: sha512-7Wctjq6f7c1CPz8sPpkwUnz8yRgVANkpNupb81q432FjcJg4l+Sw7XANdNSdWfAKq0IHI0JTcUeK5dxs/HrGPw==} + engines: {node: '>= 20.12.0'} + + '@clack/prompts@1.5.0': + resolution: {integrity: sha512-wKh+wTjmrUoUdkZg8KpJO5X+p9PWV+KE9mePseq9UYWkukgTKsGS47RRL2HstwVcvDQH+PenrPJWII8+MfiiyA==} + engines: {node: '>= 20.12.0'} + + '@ctrl/tinycolor@4.2.0': + resolution: {integrity: sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==} + engines: {node: '>=14'} + + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@expressive-code/core@0.42.0': + resolution: {integrity: sha512-MN11+9nfmaC7sYu2BZJXAXqwkBRt8t1xTSqP+Ti1NfTEskgl6xUnzDxoaiQkg0BMzpglA0pys4dpDKquP/cyIw==} + + '@expressive-code/plugin-frames@0.42.0': + resolution: {integrity: sha512-XtkPm+941Uta7Y+81Acv+OA/20F1NJmJhCX6UYGKpqEIGqplNh3PTOhcURp6tcruhlzJcWcvpWy6Oigz3SrjqA==} + + '@expressive-code/plugin-shiki@0.42.0': + resolution: {integrity: sha512-PMKey/kLmewttAHQezL+Y5Fx3vVssfDi3+FJOYQQS2mXP3tQspFELtKKAfsXfmSXdToZYgwoO69HJndqfE+09g==} + + '@expressive-code/plugin-text-markers@0.42.0': + resolution: {integrity: sha512-l59lUx8fq1v5g6SpmbDjiU0+7IdfbiWnAyRmtTVSpfhyq+nZMN4UcmYyu2b9Mynhzt7Gr+O+cXyEPDNb2AVWVQ==} + + '@iconify/tools@4.2.0': + resolution: {integrity: sha512-WRxPva/ipxYkqZd1+CkEAQmd86dQmrwH0vwK89gmp2Kh2WyyVw57XbPng0NehP3x4V1LzLsXUneP1uMfTMZmUA==} + + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/utils@2.3.0': + resolution: {integrity: sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==} + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@mdx-js/mdx@3.1.1': + resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} + + '@oslojs/encoding@1.1.0': + resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} + + '@pagefind/darwin-arm64@1.5.2': + resolution: {integrity: sha512-MXpI+7HsAdPkvJ0gk9xj9g541BCqBZOBbdwj9g6lB5LCj6kSV6nqDSjzcAJwvOsfu0fjwvC8hQU+ecfhp+MpiQ==} + cpu: [arm64] + os: [darwin] + + '@pagefind/darwin-x64@1.5.2': + resolution: {integrity: sha512-IojxFWMEJe0RQ7PQ3KXQsPIImNsbpPYpoZ+QUDrL8fAl/O27IX+LVLs74/UzEZy5uA2LD8Nz1AiwKr72vrkZQw==} + cpu: [x64] + os: [darwin] + + '@pagefind/default-ui@1.5.2': + resolution: {integrity: sha512-pm1LMnQg8N2B3n2TnjKlhaFihpz6zTiA4HiGQ6/slKO/+8K9CAU5kcjdSSPgpuk1PMuuN4hxLipUIifnrkl3Sg==} + + '@pagefind/freebsd-x64@1.5.2': + resolution: {integrity: sha512-7EVzo9+0w+2cbe671BtMj10UlNo83I+HrLVLfRxO731svHRJKUfJ/mo05gU14pe9PCfpKNQT8FS3Xc/oDN6pOA==} + cpu: [x64] + os: [freebsd] + + '@pagefind/linux-arm64@1.5.2': + resolution: {integrity: sha512-Ovt9+K35sqzn8H3ZMXGwls4TD/wMJuvRtShHIsmUQREmaxjrDEX7gHckRCrwYJ4XE1H1p6HkLz3wukrAnsfXQw==} + cpu: [arm64] + os: [linux] + + '@pagefind/linux-x64@1.5.2': + resolution: {integrity: sha512-V+tFqHKXhQKq/WqPBD67AFy7scn1/aZID00ws4fSDd+1daSi5UHR9VVlRrOUYKxn3VuFQYRD7lYXdZK1WED1YA==} + cpu: [x64] + os: [linux] + + '@pagefind/windows-arm64@1.5.2': + resolution: {integrity: sha512-hN9Nh90fNW61nNRCW9ZyQrAj/mD0eRvmJ8NlTUzkbuW8kIzGJUi3cxjFkEcMZ5h/8FsKWD/VcouZl4yo1F7B6g==} + cpu: [arm64] + os: [win32] + + '@pagefind/windows-x64@1.5.2': + resolution: {integrity: sha512-Fa2Iyw7kaDRzGMfNYNUXNW2zbL5FQVDgSOcbDHdzBrDEdpqOqg8TcZ68F22ol6NJ9IGzvUdmeyZypLW5dyhqsg==} + cpu: [x64] + os: [win32] + + '@rollup/pluginutils@5.4.0': + resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.60.4': + resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.60.4': + resolution: {integrity: sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.60.4': + resolution: {integrity: sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.60.4': + resolution: {integrity: sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.60.4': + resolution: {integrity: sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.60.4': + resolution: {integrity: sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.60.4': + resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.60.4': + resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.60.4': + resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.60.4': + resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.60.4': + resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.60.4': + resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.60.4': + resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.60.4': + resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.60.4': + resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.60.4': + resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.60.4': + resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.60.4': + resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openbsd-x64@4.60.4': + resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.60.4': + resolution: {integrity: sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.60.4': + resolution: {integrity: sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.60.4': + resolution: {integrity: sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.60.4': + resolution: {integrity: sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.60.4': + resolution: {integrity: sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==} + cpu: [x64] + os: [win32] + + '@shikijs/core@4.1.0': + resolution: {integrity: sha512-jLJtSJeuFffqX6/inRE1zqU5aFv2hrszvYgq3OjbAgFRZiWv7abKMDdQzYxuSDfmUPQozZvI/kuy6VMTvnvqTQ==} + engines: {node: '>=20'} + + '@shikijs/engine-javascript@4.1.0': + resolution: {integrity: sha512-YquhawCUgaBfhsS72e2Y/dI59gCBNPHu3fEO/tvLaXrTssxZrY5ddjtNLTwndrMgPo8b3IscE+xoICDzpTmlFQ==} + engines: {node: '>=20'} + + '@shikijs/engine-oniguruma@4.1.0': + resolution: {integrity: sha512-axLpjVs45YBvvINa+dJF+NPW+KtFkNXsFr4SDw2BMj9GdeMnGxVB9PQb2xXlJYovslt/nz6giedAyOANkfc7hg==} + engines: {node: '>=20'} + + '@shikijs/langs@4.1.0': + resolution: {integrity: sha512-nwOMruEkbgdZfQ/b8CgpNBVOpvG1k0N5tbmgiFeqsan401+x3ILqlzZJowSla4Agmq4hG2Uf2wh5jLTEhR8VSg==} + engines: {node: '>=20'} + + '@shikijs/primitive@4.1.0': + resolution: {integrity: sha512-zx2/2Uwj2q9X3KSyYREEhXO23xBw5WUhP4orK2lE4r+t9JGITmEe0JH+wPmJhqHpOT2bRRs6lAL945+LDvOAGw==} + engines: {node: '>=20'} + + '@shikijs/themes@1.29.2': + resolution: {integrity: sha512-i9TNZlsq4uoyqSbluIcZkmPL9Bfi3djVxRnofUHwvx/h6SRW3cwgBC5SML7vsDcWyukY0eCzVN980rqP6qNl9g==} + + '@shikijs/themes@4.1.0': + resolution: {integrity: sha512-emCcTnUM7yO2wltYbaxm+yLvcCI4+h8XBKc4KmJ7EZUXoSGjcCHifkI//R4OFit9ewpg7H2/9tjOuXrT2v/Knw==} + engines: {node: '>=20'} + + '@shikijs/types@1.29.2': + resolution: {integrity: sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw==} + + '@shikijs/types@4.1.0': + resolution: {integrity: sha512-3EQWX54fMpniOrDblzAhiwiJwpiTMW6+B9DWyUd9ska483tbayFYuw47UxwuPknI31bKnySfVQ/QW+jFL4rFdA==} + engines: {node: '>=20'} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/js-yaml@4.0.9': + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/mdx@2.0.13': + resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/nlcst@2.0.3': + resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} + + '@types/node@24.12.4': + resolution: {integrity: sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==} + + '@types/sax@1.2.7': + resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/yauzl@2.10.3': + resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + + '@ungap/structured-clone@1.3.1': + resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-iterate@2.0.1: + resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} + + astring@1.9.0: + resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} + hasBin: true + + astro-expressive-code@0.42.0: + resolution: {integrity: sha512-aiTePi2Cn0mJPYWZSzP1GcxCinX9mNtJyCCshVVPSg1yRwM7ADvFJOx0FnS440M9t65hp8JH//dc2qr22Bm4ag==} + peerDependencies: + astro: ^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta + + astro-icon@1.1.5: + resolution: {integrity: sha512-CJYS5nWOw9jz4RpGWmzNQY7D0y2ZZacH7atL2K9DeJXJVaz7/5WrxeyIxO8KASk1jCM96Q4LjRx/F3R+InjJrw==} + + astro@6.4.2: + resolution: {integrity: sha512-8H89CH2dKL5SCU99OCqdU9BGjmPkSJqaPurywj5XMo7eMFGUFD3vsNhdEKnEh4mK4LgGje3/QDTTSIIGst0G0Q==} + engines: {node: '>=22.12.0', npm: '>=9.6.5', pnpm: '>=7.1.0'} + hasBin: true + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + bcp-47-match@2.0.3: + resolution: {integrity: sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==} + + bcp-47@2.1.0: + resolution: {integrity: sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + + cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + + cheerio@1.2.0: + resolution: {integrity: sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==} + engines: {node: '>=20.18.1'} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + ci-info@4.4.0: + resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} + engines: {node: '>=8'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + collapse-white-space@2.1.0: + resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + common-ancestor-path@2.0.0: + resolution: {integrity: sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==} + engines: {node: '>= 18'} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + confbox@0.2.4: + resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} + + cookie-es@1.2.3: + resolution: {integrity: sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==} + + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + + crossws@0.3.5: + resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + css-selector-parser@3.3.0: + resolution: {integrity: sha512-Y2asgMGFqJKF4fq4xHDSlFYIkeVfRsm69lQC1q9kbEsH5XtnINTMrweLkjYMeaUgiXBy/uvKeO/a1JHTNnmB2g==} + + css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + devalue@5.8.1: + resolution: {integrity: sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + diff@8.0.4: + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} + engines: {node: '>=0.3.1'} + + direction@2.0.1: + resolution: {integrity: sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==} + hasBin: true + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dset@3.1.4: + resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} + engines: {node: '>=4'} + + encoding-sniffer@0.2.1: + resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + + es-module-lexer@2.1.0: + resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} + + esast-util-from-estree@2.0.0: + resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} + + esast-util-from-js@2.0.1: + resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} + + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + engines: {node: '>=18'} + hasBin: true + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + estree-util-attach-comments@3.0.0: + resolution: {integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==} + + estree-util-build-jsx@3.0.1: + resolution: {integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==} + + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + estree-util-scope@1.0.0: + resolution: {integrity: sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==} + + estree-util-to-js@2.0.0: + resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==} + + estree-util-visit@2.0.0: + resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + expressive-code@0.42.0: + resolution: {integrity: sha512-V5DtJLEKuj4wf9O6IRtPtRObkMVy2ggR+S0MdjrTw6m58krZnDioyhW1si3Y04c5YPeooP4nd85Yq9NwEVHS4g==} + + exsolve@1.0.8: + resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + extract-zip@2.0.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + flattie@1.1.1: + resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==} + engines: {node: '>=8'} + + fontace@0.4.1: + resolution: {integrity: sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==} + + fontkitten@1.0.3: + resolution: {integrity: sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==} + engines: {node: '>=20'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + + get-tsconfig@5.0.0-beta.4: + resolution: {integrity: sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==} + engines: {node: '>=20.20.0'} + + github-slugger@2.0.0: + resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} + + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + engines: {node: '>=18'} + + h3@1.15.11: + resolution: {integrity: sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==} + + hast-util-embedded@3.0.0: + resolution: {integrity: sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==} + + hast-util-format@1.1.0: + resolution: {integrity: sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==} + + hast-util-from-html@2.0.3: + resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} + + hast-util-from-parse5@8.0.3: + resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} + + hast-util-has-property@3.0.0: + resolution: {integrity: sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==} + + hast-util-is-body-ok-link@3.0.1: + resolution: {integrity: sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==} + + hast-util-is-element@3.0.0: + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + + hast-util-minify-whitespace@1.0.1: + resolution: {integrity: sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==} + + hast-util-parse-selector@4.0.0: + resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + + hast-util-phrasing@3.0.1: + resolution: {integrity: sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==} + + hast-util-raw@9.1.0: + resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} + + hast-util-select@6.0.4: + resolution: {integrity: sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw==} + + hast-util-to-estree@3.1.3: + resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-to-parse5@8.0.1: + resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==} + + hast-util-to-string@3.0.1: + resolution: {integrity: sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==} + + hast-util-to-text@4.0.2: + resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hastscript@9.0.1: + resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + + html-escaper@3.0.3: + resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + html-whitespace-sensitive-tag-names@3.0.1: + resolution: {integrity: sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==} + + htmlparser2@10.1.0: + resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} + + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + + i18next@26.3.0: + resolution: {integrity: sha512-gHSgGpUXVmuqE2El1W61DmxeyeTlFfZgdJRWMo9jScAn5pu7TuTuiccb1zh3E2J9hEBVGJ23+96x0ieBhfuIHA==} + peerDependencies: + typescript: ^5 || ^6 + peerDependenciesMeta: + typescript: + optional: true + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + + iron-webcrypto@1.2.1: + resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-docker@4.0.0: + resolution: {integrity: sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA==} + engines: {node: '>=20'} + hasBin: true + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-wsl@3.1.1: + resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} + engines: {node: '>=16'} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + + klona@2.0.6: + resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} + engines: {node: '>= 8'} + + kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + + local-pkg@1.2.1: + resolution: {integrity: sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==} + engines: {node: '>=14'} + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + lru-cache@11.5.1: + resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==} + engines: {node: 20 || >=22} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magicast@0.5.3: + resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==} + + markdown-extensions@2.0.0: + resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} + engines: {node: '>=16'} + + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + + mdast-util-definitions@6.0.0: + resolution: {integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==} + + mdast-util-directive@3.1.0: + resolution: {integrity: sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==} + + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdx@3.0.0: + resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + + mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-directive@4.0.0: + resolution: {integrity: sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-extension-mdx-expression@3.0.1: + resolution: {integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==} + + micromark-extension-mdx-jsx@3.0.2: + resolution: {integrity: sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==} + + micromark-extension-mdx-md@2.0.0: + resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==} + + micromark-extension-mdxjs-esm@3.0.0: + resolution: {integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==} + + micromark-extension-mdxjs@3.0.0: + resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-mdx-expression@2.0.3: + resolution: {integrity: sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-events-to-acorn@2.0.3: + resolution: {integrity: sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + neotraverse@0.6.18: + resolution: {integrity: sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==} + engines: {node: '>= 10'} + + nlcst-to-string@4.0.0: + resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==} + + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + + node-mock-http@1.0.4: + resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + + ofetch@1.5.1: + resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} + + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + oniguruma-parser@0.12.2: + resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} + + oniguruma-to-es@4.3.6: + resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} + + p-limit@7.3.0: + resolution: {integrity: sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw==} + engines: {node: '>=20'} + + p-queue@9.3.0: + resolution: {integrity: sha512-7NED7xhQ74Ngp4JP/2e0VZHp7vSWfJfqeiR92jPgxsz6m0Se4P03YoTKa9dDXyZ3r6P616gUXttrB6nnHYKang==} + engines: {node: '>=20'} + + p-timeout@7.0.1: + resolution: {integrity: sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==} + engines: {node: '>=20'} + + package-manager-detector@1.6.0: + resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} + + pagefind@1.5.2: + resolution: {integrity: sha512-XTUaK0hXMCu2jszWE584JGQT7y284TmMV9l/HX3rnG5uo3rHI/uHU56XTyyyPFjeWEBxECbAi0CaFDJOONtG0Q==} + hasBin: true + + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + + parse-latin@7.0.0: + resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==} + + parse5-htmlparser2-tree-adapter@7.1.0: + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} + + parse5-parser-stream@7.1.2: + resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + + piccolore@0.1.3: + resolution: {integrity: sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + pkg-types@2.3.1: + resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==} + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + engines: {node: ^10 || ^12 || >=14} + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + + radix3@1.1.2: + resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + + recma-build-jsx@1.0.0: + resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} + + recma-jsx@1.0.1: + resolution: {integrity: sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + recma-parse@1.0.0: + resolution: {integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==} + + recma-stringify@1.0.0: + resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.1.0: + resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + + rehype-expressive-code@0.42.0: + resolution: {integrity: sha512-8rp/1YMEVVSYbtz+bFBx+uSx3vA4i4T8RwRm5Q/IWbucQnnQqQ0hDqtmKOr8tv+59Cik6cu5aH3WPo0I7csuTA==} + + rehype-format@5.0.1: + resolution: {integrity: sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==} + + rehype-parse@9.0.1: + resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==} + + rehype-raw@7.0.0: + resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + + rehype-recma@1.0.0: + resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==} + + rehype-stringify@10.0.1: + resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==} + + rehype@13.0.2: + resolution: {integrity: sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==} + + remark-directive@4.0.0: + resolution: {integrity: sha512-7sxn4RfF1o3izevPV1DheyGDD6X4c9hrGpfdUpm7uC++dqrnJxIZVkk7CoKqcLm0VUMAuOol7Mno3m6g8cfMuA==} + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-mdx@3.1.1: + resolution: {integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-smartypants@3.0.2: + resolution: {integrity: sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==} + engines: {node: '>=16.0.0'} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + retext-latin@4.0.0: + resolution: {integrity: sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==} + + retext-smartypants@6.2.0: + resolution: {integrity: sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==} + + retext-stringify@4.0.0: + resolution: {integrity: sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==} + + retext@9.0.0: + resolution: {integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==} + + rollup@4.60.4: + resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sax@1.6.0: + resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} + engines: {node: '>=11.0.0'} + + semver@7.8.1: + resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==} + engines: {node: '>=10'} + hasBin: true + + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + shiki@4.1.0: + resolution: {integrity: sha512-l/ABZPUR5v70jI10EzqfMS/I96vjSGv2y0ihUV+WYFzv0EfvW4s54m0Lg8wCrrL+2IkwBzFTuxkZjPf8b2NX9Q==} + engines: {node: '>=20'} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + sitemap@9.0.1: + resolution: {integrity: sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==} + engines: {node: '>=20.19.5', npm: '>=10.8.2'} + hasBin: true + + smol-toml@1.6.1: + resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} + engines: {node: '>= 18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + starlight-ion-theme@2.4.0: + resolution: {integrity: sha512-7iydgTqjjLW9Gh39DSgVLqz8uHCFiqmMfHQQg3bELTkX4eLUvmqi9TOsOEoGRmeAdY9/rhcVMm78b3KHGESE5g==} + peerDependencies: + '@astrojs/starlight': ^0.38 + astro: ^6.0.0 + + stream-replace-string@2.0.0: + resolution: {integrity: sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + + svgo@3.3.3: + resolution: {integrity: sha512-+wn7I4p7YgJhHs38k2TNjy1vCfPIfLIJWR5MnCStsN8WuuTcBnRKcMHQLMM2ijxGZmDoZwNv8ipl5aTTen62ng==} + engines: {node: '>=14.0.0'} + hasBin: true + + svgo@4.0.1: + resolution: {integrity: sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==} + engines: {node: '>=16'} + hasBin: true + + tar@7.5.15: + resolution: {integrity: sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==} + engines: {node: '>=18'} + + tiny-inflate@1.0.3: + resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} + + tinyclip@0.1.13: + resolution: {integrity: sha512-8OqlXQ35euK9+e7L68u8UwcODxkHoIkjbGsgXuARKNyQ5G6xt8nw1YPeMbxMLgCPFkToU+UEK5j05t2t8edKpQ==} + engines: {node: ^16.14.0 || >= 17.3.0} + + tinyexec@1.2.3: + resolution: {integrity: sha512-g62dB+w1/OEFnPvmX0yd/HnetYITOL+1nJW7kitOycOeAvmbWC/nu0fwmmQ/kupNojqExzyC/T++pST/jRJ2mQ==} + engines: {node: '>=18'} + + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + + ultrahtml@1.6.0: + resolution: {integrity: sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==} + + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + undici@7.26.0: + resolution: {integrity: sha512-3O9Tf67pGhgOv9jM35AbhkXAKi13f3oy3aE4CSgr+TckGeY+/iu97ZXN+J7DpHPzLbVApFd1IFhcnBjREYXYcg==} + engines: {node: '>=20.18.1'} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unifont@0.7.4: + resolution: {integrity: sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg==} + + unist-util-find-after@5.0.0: + resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-modify-children@4.0.0: + resolution: {integrity: sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==} + + unist-util-position-from-estree@2.0.0: + resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-remove-position@5.0.0: + resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-children@3.0.0: + resolution: {integrity: sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + + unstorage@1.17.5: + resolution: {integrity: sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==} + peerDependencies: + '@azure/app-configuration': ^1.8.0 + '@azure/cosmos': ^4.2.0 + '@azure/data-tables': ^13.3.0 + '@azure/identity': ^4.6.0 + '@azure/keyvault-secrets': ^4.9.0 + '@azure/storage-blob': ^12.26.0 + '@capacitor/preferences': ^6 || ^7 || ^8 + '@deno/kv': '>=0.9.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.34.3 + '@vercel/blob': '>=0.27.1' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1 || ^2 || ^3 + aws4fetch: ^1.0.20 + db0: '>=0.2.1' + idb-keyval: ^6.2.1 + ioredis: ^5.4.2 + uploadthing: ^7.4.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/functions': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + uploadthing: + optional: true + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vfile-location@5.0.3: + resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite@7.3.3: + resolution: {integrity: sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@1.1.3: + resolution: {integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + vite: + optional: true + + web-namespaces@2.0.1: + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + which-pm-runs@1.1.0: + resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==} + engines: {node: '>=4'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + xxhash-wasm@1.1.0: + resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + + yargs-parser@22.0.0: + resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} + + yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + + yocto-queue@1.2.2: + resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} + engines: {node: '>=12.20'} + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@antfu/install-pkg@1.1.0': + dependencies: + package-manager-detector: 1.6.0 + tinyexec: 1.2.3 + + '@antfu/utils@8.1.1': {} + + '@astrojs/compiler@4.0.0': {} + + '@astrojs/internal-helpers@0.10.0': + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + js-yaml: 4.1.1 + picomatch: 4.0.4 + retext-smartypants: 6.2.0 + shiki: 4.1.0 + smol-toml: 1.6.1 + unified: 11.0.5 + + '@astrojs/internal-helpers@0.9.1': + dependencies: + picomatch: 4.0.4 + + '@astrojs/markdown-remark@7.1.2': + dependencies: + '@astrojs/internal-helpers': 0.9.1 + '@astrojs/prism': 4.0.2 + github-slugger: 2.0.0 + hast-util-from-html: 2.0.3 + hast-util-to-text: 4.0.2 + js-yaml: 4.1.1 + mdast-util-definitions: 6.0.0 + rehype-raw: 7.0.0 + rehype-stringify: 10.0.1 + remark-gfm: 4.0.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + remark-smartypants: 3.0.2 + retext-smartypants: 6.2.0 + shiki: 4.1.0 + smol-toml: 1.6.1 + unified: 11.0.5 + unist-util-remove-position: 5.0.0 + unist-util-visit: 5.1.0 + unist-util-visit-parents: 6.0.2 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@astrojs/markdown-remark@7.2.0': + dependencies: + '@astrojs/internal-helpers': 0.10.0 + '@astrojs/prism': 4.0.2 + github-slugger: 2.0.0 + hast-util-from-html: 2.0.3 + hast-util-to-text: 4.0.2 + mdast-util-definitions: 6.0.0 + rehype-raw: 7.0.0 + rehype-stringify: 10.0.1 + remark-gfm: 4.0.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + remark-smartypants: 3.0.2 + unified: 11.0.5 + unist-util-remove-position: 5.0.0 + unist-util-visit: 5.1.0 + unist-util-visit-parents: 6.0.2 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@astrojs/mdx@5.0.6(astro@6.4.2(@types/node@24.12.4)(rollup@4.60.4))': + dependencies: + '@astrojs/markdown-remark': 7.1.2 + '@mdx-js/mdx': 3.1.1 + acorn: 8.16.0 + astro: 6.4.2(@types/node@24.12.4)(rollup@4.60.4) + es-module-lexer: 2.1.0 + estree-util-visit: 2.0.0 + hast-util-to-html: 9.0.5 + piccolore: 0.1.3 + rehype-raw: 7.0.0 + remark-gfm: 4.0.1 + remark-smartypants: 3.0.2 + source-map: 0.7.6 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@astrojs/prism@4.0.2': + dependencies: + prismjs: 1.30.0 + + '@astrojs/sitemap@3.7.3': + dependencies: + sitemap: 9.0.1 + stream-replace-string: 2.0.0 + zod: 4.4.3 + + '@astrojs/starlight@0.39.2(astro@6.4.2(@types/node@24.12.4)(rollup@4.60.4))': + dependencies: + '@astrojs/markdown-remark': 7.2.0 + '@astrojs/mdx': 5.0.6(astro@6.4.2(@types/node@24.12.4)(rollup@4.60.4)) + '@astrojs/sitemap': 3.7.3 + '@pagefind/default-ui': 1.5.2 + '@types/hast': 3.0.4 + '@types/js-yaml': 4.0.9 + '@types/mdast': 4.0.4 + astro: 6.4.2(@types/node@24.12.4)(rollup@4.60.4) + astro-expressive-code: 0.42.0(astro@6.4.2(@types/node@24.12.4)(rollup@4.60.4)) + bcp-47: 2.1.0 + hast-util-from-html: 2.0.3 + hast-util-select: 6.0.4 + hast-util-to-string: 3.0.1 + hastscript: 9.0.1 + i18next: 26.3.0 + js-yaml: 4.1.1 + klona: 2.0.6 + magic-string: 0.30.21 + mdast-util-directive: 3.1.0 + mdast-util-to-markdown: 2.1.2 + mdast-util-to-string: 4.0.0 + pagefind: 1.5.2 + rehype: 13.0.2 + rehype-format: 5.0.1 + remark-directive: 4.0.0 + ultrahtml: 1.6.0 + unified: 11.0.5 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + - typescript + + '@astrojs/telemetry@3.3.2': + dependencies: + ci-info: 4.4.0 + dset: 3.1.4 + is-docker: 4.0.0 + is-wsl: 3.1.1 + which-pm-runs: 1.1.0 + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@capsizecss/unpack@4.0.0': + dependencies: + fontkitten: 1.0.3 + + '@clack/core@1.4.0': + dependencies: + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 + + '@clack/prompts@1.5.0': + dependencies: + '@clack/core': 1.4.0 + fast-string-width: 3.0.2 + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 + + '@ctrl/tinycolor@4.2.0': {} + + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.27.7': + optional: true + + '@esbuild/android-arm64@0.27.7': + optional: true + + '@esbuild/android-arm@0.27.7': + optional: true + + '@esbuild/android-x64@0.27.7': + optional: true + + '@esbuild/darwin-arm64@0.27.7': + optional: true + + '@esbuild/darwin-x64@0.27.7': + optional: true + + '@esbuild/freebsd-arm64@0.27.7': + optional: true + + '@esbuild/freebsd-x64@0.27.7': + optional: true + + '@esbuild/linux-arm64@0.27.7': + optional: true + + '@esbuild/linux-arm@0.27.7': + optional: true + + '@esbuild/linux-ia32@0.27.7': + optional: true + + '@esbuild/linux-loong64@0.27.7': + optional: true + + '@esbuild/linux-mips64el@0.27.7': + optional: true + + '@esbuild/linux-ppc64@0.27.7': + optional: true + + '@esbuild/linux-riscv64@0.27.7': + optional: true + + '@esbuild/linux-s390x@0.27.7': + optional: true + + '@esbuild/linux-x64@0.27.7': + optional: true + + '@esbuild/netbsd-arm64@0.27.7': + optional: true + + '@esbuild/netbsd-x64@0.27.7': + optional: true + + '@esbuild/openbsd-arm64@0.27.7': + optional: true + + '@esbuild/openbsd-x64@0.27.7': + optional: true + + '@esbuild/openharmony-arm64@0.27.7': + optional: true + + '@esbuild/sunos-x64@0.27.7': + optional: true + + '@esbuild/win32-arm64@0.27.7': + optional: true + + '@esbuild/win32-ia32@0.27.7': + optional: true + + '@esbuild/win32-x64@0.27.7': + optional: true + + '@expressive-code/core@0.42.0': + dependencies: + '@ctrl/tinycolor': 4.2.0 + hast-util-select: 6.0.4 + hast-util-to-html: 9.0.5 + hast-util-to-text: 4.0.2 + hastscript: 9.0.1 + postcss: 8.5.15 + postcss-nested: 6.2.0(postcss@8.5.15) + unist-util-visit: 5.1.0 + unist-util-visit-parents: 6.0.2 + + '@expressive-code/plugin-frames@0.42.0': + dependencies: + '@expressive-code/core': 0.42.0 + + '@expressive-code/plugin-shiki@0.42.0': + dependencies: + '@expressive-code/core': 0.42.0 + shiki: 4.1.0 + + '@expressive-code/plugin-text-markers@0.42.0': + dependencies: + '@expressive-code/core': 0.42.0 + + '@iconify/tools@4.2.0': + dependencies: + '@iconify/types': 2.0.0 + '@iconify/utils': 2.3.0 + cheerio: 1.2.0 + domhandler: 5.0.3 + extract-zip: 2.0.1 + local-pkg: 1.2.1 + pathe: 2.0.3 + svgo: 3.3.3 + tar: 7.5.15 + transitivePeerDependencies: + - supports-color + + '@iconify/types@2.0.0': {} + + '@iconify/utils@2.3.0': + dependencies: + '@antfu/install-pkg': 1.1.0 + '@antfu/utils': 8.1.1 + '@iconify/types': 2.0.0 + debug: 4.4.3 + globals: 15.15.0 + kolorist: 1.8.0 + local-pkg: 1.2.1 + mlly: 1.8.2 + transitivePeerDependencies: + - supports-color + + '@img/colour@1.1.0': {} + + '@img/sharp-darwin-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.4 + optional: true + + '@img/sharp-darwin-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 + optional: true + + '@img/sharp-linux-arm@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.4 + optional: true + + '@img/sharp-linux-ppc64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.4 + optional: true + + '@img/sharp-linux-riscv64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.2.4 + optional: true + + '@img/sharp-linux-s390x@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.4 + optional: true + + '@img/sharp-linux-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.34.5': + dependencies: + '@emnapi/runtime': 1.10.0 + optional: true + + '@img/sharp-win32-arm64@0.34.5': + optional: true + + '@img/sharp-win32-ia32@0.34.5': + optional: true + + '@img/sharp-win32-x64@0.34.5': + optional: true + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.3 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@mdx-js/mdx@3.1.1': + dependencies: + '@types/estree': 1.0.9 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdx': 2.0.13 + acorn: 8.16.0 + collapse-white-space: 2.1.0 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-util-scope: 1.0.0 + estree-walker: 3.0.3 + hast-util-to-jsx-runtime: 2.3.6 + markdown-extensions: 2.0.0 + recma-build-jsx: 1.0.0 + recma-jsx: 1.0.1(acorn@8.16.0) + recma-stringify: 1.0.0 + rehype-recma: 1.0.0 + remark-mdx: 3.1.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + source-map: 0.7.6 + unified: 11.0.5 + unist-util-position-from-estree: 2.0.0 + unist-util-stringify-position: 4.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@oslojs/encoding@1.1.0': {} + + '@pagefind/darwin-arm64@1.5.2': + optional: true + + '@pagefind/darwin-x64@1.5.2': + optional: true + + '@pagefind/default-ui@1.5.2': {} + + '@pagefind/freebsd-x64@1.5.2': + optional: true + + '@pagefind/linux-arm64@1.5.2': + optional: true + + '@pagefind/linux-x64@1.5.2': + optional: true + + '@pagefind/windows-arm64@1.5.2': + optional: true + + '@pagefind/windows-x64@1.5.2': + optional: true + + '@rollup/pluginutils@5.4.0(rollup@4.60.4)': + dependencies: + '@types/estree': 1.0.9 + estree-walker: 2.0.2 + picomatch: 4.0.4 + optionalDependencies: + rollup: 4.60.4 + + '@rollup/rollup-android-arm-eabi@4.60.4': + optional: true + + '@rollup/rollup-android-arm64@4.60.4': + optional: true + + '@rollup/rollup-darwin-arm64@4.60.4': + optional: true + + '@rollup/rollup-darwin-x64@4.60.4': + optional: true + + '@rollup/rollup-freebsd-arm64@4.60.4': + optional: true + + '@rollup/rollup-freebsd-x64@4.60.4': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.60.4': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-x64-musl@4.60.4': + optional: true + + '@rollup/rollup-openbsd-x64@4.60.4': + optional: true + + '@rollup/rollup-openharmony-arm64@4.60.4': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.60.4': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.60.4': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.60.4': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.60.4': + optional: true + + '@shikijs/core@4.1.0': + dependencies: + '@shikijs/primitive': 4.1.0 + '@shikijs/types': 4.1.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@4.1.0': + dependencies: + '@shikijs/types': 4.1.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.6 + + '@shikijs/engine-oniguruma@4.1.0': + dependencies: + '@shikijs/types': 4.1.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@4.1.0': + dependencies: + '@shikijs/types': 4.1.0 + + '@shikijs/primitive@4.1.0': + dependencies: + '@shikijs/types': 4.1.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/themes@1.29.2': + dependencies: + '@shikijs/types': 1.29.2 + + '@shikijs/themes@4.1.0': + dependencies: + '@shikijs/types': 4.1.0 + + '@shikijs/types@1.29.2': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/types@4.1.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.9 + + '@types/estree@1.0.8': {} + + '@types/estree@1.0.9': {} + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/js-yaml@4.0.9': {} + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdx@2.0.13': {} + + '@types/ms@2.1.0': {} + + '@types/nlcst@2.0.3': + dependencies: + '@types/unist': 3.0.3 + + '@types/node@24.12.4': + dependencies: + undici-types: 7.16.0 + + '@types/sax@1.2.7': + dependencies: + '@types/node': 24.12.4 + + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@types/yauzl@2.10.3': + dependencies: + '@types/node': 24.12.4 + optional: true + + '@ungap/structured-clone@1.3.1': {} + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + + arg@5.0.2: {} + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + array-iterate@2.0.1: {} + + astring@1.9.0: {} + + astro-expressive-code@0.42.0(astro@6.4.2(@types/node@24.12.4)(rollup@4.60.4)): + dependencies: + astro: 6.4.2(@types/node@24.12.4)(rollup@4.60.4) + rehype-expressive-code: 0.42.0 + + astro-icon@1.1.5: + dependencies: + '@iconify/tools': 4.2.0 + '@iconify/types': 2.0.0 + '@iconify/utils': 2.3.0 + transitivePeerDependencies: + - supports-color + + astro@6.4.2(@types/node@24.12.4)(rollup@4.60.4): + dependencies: + '@astrojs/compiler': 4.0.0 + '@astrojs/internal-helpers': 0.10.0 + '@astrojs/markdown-remark': 7.2.0 + '@astrojs/telemetry': 3.3.2 + '@capsizecss/unpack': 4.0.0 + '@clack/prompts': 1.5.0 + '@oslojs/encoding': 1.1.0 + '@rollup/pluginutils': 5.4.0(rollup@4.60.4) + aria-query: 5.3.2 + axobject-query: 4.1.0 + ci-info: 4.4.0 + clsx: 2.1.1 + common-ancestor-path: 2.0.0 + cookie: 1.1.1 + devalue: 5.8.1 + diff: 8.0.4 + dset: 3.1.4 + es-module-lexer: 2.1.0 + esbuild: 0.27.7 + flattie: 1.1.1 + fontace: 0.4.1 + get-tsconfig: 5.0.0-beta.4 + github-slugger: 2.0.0 + html-escaper: 3.0.3 + http-cache-semantics: 4.2.0 + js-yaml: 4.1.1 + jsonc-parser: 3.3.1 + magic-string: 0.30.21 + magicast: 0.5.3 + mrmime: 2.0.1 + neotraverse: 0.6.18 + obug: 2.1.1 + p-limit: 7.3.0 + p-queue: 9.3.0 + package-manager-detector: 1.6.0 + piccolore: 0.1.3 + picomatch: 4.0.4 + rehype: 13.0.2 + semver: 7.8.1 + shiki: 4.1.0 + smol-toml: 1.6.1 + svgo: 4.0.1 + tinyclip: 0.1.13 + tinyexec: 1.2.3 + tinyglobby: 0.2.16 + ultrahtml: 1.6.0 + unifont: 0.7.4 + unist-util-visit: 5.1.0 + unstorage: 1.17.5 + vfile: 6.0.3 + vite: 7.3.3(@types/node@24.12.4) + vitefu: 1.1.3(vite@7.3.3(@types/node@24.12.4)) + xxhash-wasm: 1.1.0 + yargs-parser: 22.0.0 + zod: 4.4.3 + optionalDependencies: + sharp: 0.34.5 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@types/node' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - idb-keyval + - ioredis + - jiti + - less + - lightningcss + - rollup + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - uploadthing + - yaml + + axobject-query@4.1.0: {} + + bail@2.0.2: {} + + bcp-47-match@2.0.3: {} + + bcp-47@2.1.0: + dependencies: + is-alphabetical: 2.0.1 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + + boolbase@1.0.0: {} + + buffer-crc32@0.2.13: {} + + ccount@2.0.1: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + + cheerio-select@2.1.0: + dependencies: + boolbase: 1.0.0 + css-select: 5.2.2 + css-what: 6.2.2 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + + cheerio@1.2.0: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + encoding-sniffer: 0.2.1 + htmlparser2: 10.1.0 + parse5: 7.3.0 + parse5-htmlparser2-tree-adapter: 7.1.0 + parse5-parser-stream: 7.1.2 + undici: 7.26.0 + whatwg-mimetype: 4.0.0 + + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + + chownr@3.0.0: {} + + ci-info@4.4.0: {} + + clsx@2.1.1: {} + + collapse-white-space@2.1.0: {} + + comma-separated-tokens@2.0.3: {} + + commander@11.1.0: {} + + commander@7.2.0: {} + + common-ancestor-path@2.0.0: {} + + confbox@0.1.8: {} + + confbox@0.2.4: {} + + cookie-es@1.2.3: {} + + cookie@1.1.1: {} + + crossws@0.3.5: + dependencies: + uncrypto: 0.1.3 + + css-select@5.2.2: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-selector-parser@3.3.0: {} + + css-tree@2.2.1: + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.1 + + css-tree@2.3.1: + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.2.1 + + css-tree@3.2.1: + dependencies: + mdn-data: 2.27.1 + source-map-js: 1.2.1 + + css-what@6.2.2: {} + + cssesc@3.0.0: {} + + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + + defu@6.1.7: {} + + dequal@2.0.3: {} + + destr@2.0.5: {} + + detect-libc@2.1.2: {} + + devalue@5.8.1: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + diff@8.0.4: {} + + direction@2.0.1: {} + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dset@3.1.4: {} + + encoding-sniffer@0.2.1: + dependencies: + iconv-lite: 0.6.3 + whatwg-encoding: 3.1.1 + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + entities@4.5.0: {} + + entities@6.0.1: {} + + entities@7.0.1: {} + + es-module-lexer@2.1.0: {} + + esast-util-from-estree@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + unist-util-position-from-estree: 2.0.0 + + esast-util-from-js@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + acorn: 8.16.0 + esast-util-from-estree: 2.0.0 + vfile-message: 4.0.3 + + esbuild@0.27.7: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 + + escape-string-regexp@5.0.0: {} + + estree-util-attach-comments@3.0.0: + dependencies: + '@types/estree': 1.0.9 + + estree-util-build-jsx@3.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-walker: 3.0.3 + + estree-util-is-identifier-name@3.0.0: {} + + estree-util-scope@1.0.0: + dependencies: + '@types/estree': 1.0.9 + devlop: 1.1.0 + + estree-util-to-js@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + astring: 1.9.0 + source-map: 0.7.6 + + estree-util-visit@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/unist': 3.0.3 + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + eventemitter3@5.0.4: {} + + expressive-code@0.42.0: + dependencies: + '@expressive-code/core': 0.42.0 + '@expressive-code/plugin-frames': 0.42.0 + '@expressive-code/plugin-shiki': 0.42.0 + '@expressive-code/plugin-text-markers': 0.42.0 + + exsolve@1.0.8: {} + + extend@3.0.2: {} + + extract-zip@2.0.1: + dependencies: + debug: 4.4.3 + get-stream: 5.2.0 + yauzl: 2.10.0 + optionalDependencies: + '@types/yauzl': 2.10.3 + transitivePeerDependencies: + - supports-color + + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + + fast-wrap-ansi@0.2.2: + dependencies: + fast-string-width: 3.0.2 + + fd-slicer@1.1.0: + dependencies: + pend: 1.2.0 + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + flattie@1.1.1: {} + + fontace@0.4.1: + dependencies: + fontkitten: 1.0.3 + + fontkitten@1.0.3: + dependencies: + tiny-inflate: 1.0.3 + + fsevents@2.3.3: + optional: true + + get-stream@5.2.0: + dependencies: + pump: 3.0.4 + + get-tsconfig@5.0.0-beta.4: + dependencies: + resolve-pkg-maps: 1.0.0 + + github-slugger@2.0.0: {} + + globals@15.15.0: {} + + h3@1.15.11: + dependencies: + cookie-es: 1.2.3 + crossws: 0.3.5 + defu: 6.1.7 + destr: 2.0.5 + iron-webcrypto: 1.2.1 + node-mock-http: 1.0.4 + radix3: 1.1.2 + ufo: 1.6.4 + uncrypto: 0.1.3 + + hast-util-embedded@3.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-is-element: 3.0.0 + + hast-util-format@1.1.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-embedded: 3.0.0 + hast-util-minify-whitespace: 1.0.1 + hast-util-phrasing: 3.0.1 + hast-util-whitespace: 3.0.0 + html-whitespace-sensitive-tag-names: 3.0.1 + unist-util-visit-parents: 6.0.2 + + hast-util-from-html@2.0.3: + dependencies: + '@types/hast': 3.0.4 + devlop: 1.1.0 + hast-util-from-parse5: 8.0.3 + parse5: 7.3.0 + vfile: 6.0.3 + vfile-message: 4.0.3 + + hast-util-from-parse5@8.0.3: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + devlop: 1.1.0 + hastscript: 9.0.1 + property-information: 7.1.0 + vfile: 6.0.3 + vfile-location: 5.0.3 + web-namespaces: 2.0.1 + + hast-util-has-property@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-is-body-ok-link@3.0.1: + dependencies: + '@types/hast': 3.0.4 + + hast-util-is-element@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-minify-whitespace@1.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-embedded: 3.0.0 + hast-util-is-element: 3.0.0 + hast-util-whitespace: 3.0.0 + unist-util-is: 6.0.1 + + hast-util-parse-selector@4.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-phrasing@3.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-embedded: 3.0.0 + hast-util-has-property: 3.0.0 + hast-util-is-body-ok-link: 3.0.1 + hast-util-is-element: 3.0.0 + + hast-util-raw@9.1.0: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + '@ungap/structured-clone': 1.3.1 + hast-util-from-parse5: 8.0.3 + hast-util-to-parse5: 8.0.1 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + parse5: 7.3.0 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-select@6.0.4: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + bcp-47-match: 2.0.3 + comma-separated-tokens: 2.0.3 + css-selector-parser: 3.3.0 + devlop: 1.1.0 + direction: 2.0.1 + hast-util-has-property: 3.0.0 + hast-util-to-string: 3.0.1 + hast-util-whitespace: 3.0.0 + nth-check: 2.1.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + hast-util-to-estree@3.1.3: + dependencies: + '@types/estree': 1.0.9 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-attach-comments: 3.0.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + zwitch: 2.0.4 + transitivePeerDependencies: + - supports-color + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.9 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-to-parse5@8.0.1: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-string@3.0.1: + dependencies: + '@types/hast': 3.0.4 + + hast-util-to-text@4.0.2: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + hast-util-is-element: 3.0.0 + unist-util-find-after: 5.0.0 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hastscript@9.0.1: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 4.0.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + + html-escaper@3.0.3: {} + + html-void-elements@3.0.0: {} + + html-whitespace-sensitive-tag-names@3.0.1: {} + + htmlparser2@10.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 7.0.1 + + http-cache-semantics@4.2.0: {} + + i18next@26.3.0: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + inline-style-parser@0.2.7: {} + + iron-webcrypto@1.2.1: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-decimal@2.0.1: {} + + is-docker@3.0.0: {} + + is-docker@4.0.0: {} + + is-hexadecimal@2.0.1: {} + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-plain-obj@4.1.0: {} + + is-wsl@3.1.1: + dependencies: + is-inside-container: 1.0.0 + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsonc-parser@3.3.1: {} + + klona@2.0.6: {} + + kolorist@1.8.0: {} + + local-pkg@1.2.1: + dependencies: + mlly: 1.8.2 + pkg-types: 2.3.1 + quansync: 0.2.11 + + longest-streak@3.1.0: {} + + lru-cache@11.5.1: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magicast@0.5.3: + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + source-map-js: 1.2.1 + + markdown-extensions@2.0.0: {} + + markdown-table@3.0.4: {} + + mdast-util-definitions@6.0.0: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + unist-util-visit: 5.1.0 + + mdast-util-directive@3.1.0: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-visit-parents: 6.0.2 + transitivePeerDependencies: + - supports-color + + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + mdast-util-from-markdown@2.0.3: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.3 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx@3.0.0: + dependencies: + mdast-util-from-markdown: 2.0.3 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.1 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + mdn-data@2.0.28: {} + + mdn-data@2.0.30: {} + + mdn-data@2.27.1: {} + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-directive@4.0.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + parse-entities: 4.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-mdx-expression@3.0.1: + dependencies: + '@types/estree': 1.0.9 + devlop: 1.1.0 + micromark-factory-mdx-expression: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-mdx-jsx@3.0.2: + dependencies: + '@types/estree': 1.0.9 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + micromark-factory-mdx-expression: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + vfile-message: 4.0.3 + + micromark-extension-mdx-md@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-mdxjs-esm@3.0.0: + dependencies: + '@types/estree': 1.0.9 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.3 + + micromark-extension-mdxjs@3.0.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + micromark-extension-mdx-expression: 3.0.1 + micromark-extension-mdx-jsx: 3.0.2 + micromark-extension-mdx-md: 2.0.0 + micromark-extension-mdxjs-esm: 3.0.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-mdx-expression@2.0.3: + dependencies: + '@types/estree': 1.0.9 + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.3 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.3.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-events-to-acorn@2.0.3: + dependencies: + '@types/estree': 1.0.9 + '@types/unist': 3.0.3 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + vfile-message: 4.0.3 + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.13 + debug: 4.4.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + + minipass@7.1.3: {} + + minizlib@3.1.0: + dependencies: + minipass: 7.1.3 + + mlly@1.8.2: + dependencies: + acorn: 8.16.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.4 + + mrmime@2.0.1: {} + + ms@2.1.3: {} + + nanoid@3.3.12: {} + + neotraverse@0.6.18: {} + + nlcst-to-string@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + + node-fetch-native@1.6.7: {} + + node-mock-http@1.0.4: {} + + normalize-path@3.0.0: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + obug@2.1.1: {} + + ofetch@1.5.1: + dependencies: + destr: 2.0.5 + node-fetch-native: 1.6.7 + ufo: 1.6.4 + + ohash@2.0.11: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + oniguruma-parser@0.12.2: {} + + oniguruma-to-es@4.3.6: + dependencies: + oniguruma-parser: 0.12.2 + regex: 6.1.0 + regex-recursion: 6.0.2 + + p-limit@7.3.0: + dependencies: + yocto-queue: 1.2.2 + + p-queue@9.3.0: + dependencies: + eventemitter3: 5.0.4 + p-timeout: 7.0.1 + + p-timeout@7.0.1: {} + + package-manager-detector@1.6.0: {} + + pagefind@1.5.2: + optionalDependencies: + '@pagefind/darwin-arm64': 1.5.2 + '@pagefind/darwin-x64': 1.5.2 + '@pagefind/freebsd-x64': 1.5.2 + '@pagefind/linux-arm64': 1.5.2 + '@pagefind/linux-x64': 1.5.2 + '@pagefind/windows-arm64': 1.5.2 + '@pagefind/windows-x64': 1.5.2 + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.3.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + parse-latin@7.0.0: + dependencies: + '@types/nlcst': 2.0.3 + '@types/unist': 3.0.3 + nlcst-to-string: 4.0.0 + unist-util-modify-children: 4.0.0 + unist-util-visit-children: 3.0.0 + vfile: 6.0.3 + + parse5-htmlparser2-tree-adapter@7.1.0: + dependencies: + domhandler: 5.0.3 + parse5: 7.3.0 + + parse5-parser-stream@7.1.2: + dependencies: + parse5: 7.3.0 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + pathe@2.0.3: {} + + pend@1.2.0: {} + + piccolore@0.1.3: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.4: {} + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.2 + pathe: 2.0.3 + + pkg-types@2.3.1: + dependencies: + confbox: 0.2.4 + exsolve: 1.0.8 + pathe: 2.0.3 + + postcss-nested@6.2.0(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + postcss-selector-parser: 6.1.2 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss@8.5.15: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prismjs@1.30.0: {} + + property-information@7.1.0: {} + + pump@3.0.4: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + quansync@0.2.11: {} + + radix3@1.1.2: {} + + readdirp@5.0.0: {} + + recma-build-jsx@1.0.0: + dependencies: + '@types/estree': 1.0.9 + estree-util-build-jsx: 3.0.1 + vfile: 6.0.3 + + recma-jsx@1.0.1(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + estree-util-to-js: 2.0.0 + recma-parse: 1.0.0 + recma-stringify: 1.0.0 + unified: 11.0.5 + + recma-parse@1.0.0: + dependencies: + '@types/estree': 1.0.9 + esast-util-from-js: 2.0.1 + unified: 11.0.5 + vfile: 6.0.3 + + recma-stringify@1.0.0: + dependencies: + '@types/estree': 1.0.9 + estree-util-to-js: 2.0.0 + unified: 11.0.5 + vfile: 6.0.3 + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.1.0: + dependencies: + regex-utilities: 2.3.0 + + rehype-expressive-code@0.42.0: + dependencies: + expressive-code: 0.42.0 + + rehype-format@5.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-format: 1.1.0 + + rehype-parse@9.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-from-html: 2.0.3 + unified: 11.0.5 + + rehype-raw@7.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-raw: 9.1.0 + vfile: 6.0.3 + + rehype-recma@1.0.0: + dependencies: + '@types/estree': 1.0.9 + '@types/hast': 3.0.4 + hast-util-to-estree: 3.1.3 + transitivePeerDependencies: + - supports-color + + rehype-stringify@10.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + unified: 11.0.5 + + rehype@13.0.2: + dependencies: + '@types/hast': 3.0.4 + rehype-parse: 9.0.1 + rehype-stringify: 10.0.1 + unified: 11.0.5 + + remark-directive@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-directive: 3.1.0 + micromark-extension-directive: 4.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-mdx@3.1.1: + dependencies: + mdast-util-mdx: 3.0.0 + micromark-extension-mdxjs: 3.0.0 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + + remark-smartypants@3.0.2: + dependencies: + retext: 9.0.0 + retext-smartypants: 6.2.0 + unified: 11.0.5 + unist-util-visit: 5.1.0 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + resolve-pkg-maps@1.0.0: {} + + retext-latin@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + parse-latin: 7.0.0 + unified: 11.0.5 + + retext-smartypants@6.2.0: + dependencies: + '@types/nlcst': 2.0.3 + nlcst-to-string: 4.0.0 + unist-util-visit: 5.1.0 + + retext-stringify@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + nlcst-to-string: 4.0.0 + unified: 11.0.5 + + retext@9.0.0: + dependencies: + '@types/nlcst': 2.0.3 + retext-latin: 4.0.0 + retext-stringify: 4.0.0 + unified: 11.0.5 + + rollup@4.60.4: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.60.4 + '@rollup/rollup-android-arm64': 4.60.4 + '@rollup/rollup-darwin-arm64': 4.60.4 + '@rollup/rollup-darwin-x64': 4.60.4 + '@rollup/rollup-freebsd-arm64': 4.60.4 + '@rollup/rollup-freebsd-x64': 4.60.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.4 + '@rollup/rollup-linux-arm-musleabihf': 4.60.4 + '@rollup/rollup-linux-arm64-gnu': 4.60.4 + '@rollup/rollup-linux-arm64-musl': 4.60.4 + '@rollup/rollup-linux-loong64-gnu': 4.60.4 + '@rollup/rollup-linux-loong64-musl': 4.60.4 + '@rollup/rollup-linux-ppc64-gnu': 4.60.4 + '@rollup/rollup-linux-ppc64-musl': 4.60.4 + '@rollup/rollup-linux-riscv64-gnu': 4.60.4 + '@rollup/rollup-linux-riscv64-musl': 4.60.4 + '@rollup/rollup-linux-s390x-gnu': 4.60.4 + '@rollup/rollup-linux-x64-gnu': 4.60.4 + '@rollup/rollup-linux-x64-musl': 4.60.4 + '@rollup/rollup-openbsd-x64': 4.60.4 + '@rollup/rollup-openharmony-arm64': 4.60.4 + '@rollup/rollup-win32-arm64-msvc': 4.60.4 + '@rollup/rollup-win32-ia32-msvc': 4.60.4 + '@rollup/rollup-win32-x64-gnu': 4.60.4 + '@rollup/rollup-win32-x64-msvc': 4.60.4 + fsevents: 2.3.3 + + safer-buffer@2.1.2: {} + + sax@1.6.0: {} + + semver@7.8.1: {} + + sharp@0.34.5: + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.1 + 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 + + shiki@4.1.0: + dependencies: + '@shikijs/core': 4.1.0 + '@shikijs/engine-javascript': 4.1.0 + '@shikijs/engine-oniguruma': 4.1.0 + '@shikijs/langs': 4.1.0 + '@shikijs/themes': 4.1.0 + '@shikijs/types': 4.1.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + sisteransi@1.0.5: {} + + sitemap@9.0.1: + dependencies: + '@types/node': 24.12.4 + '@types/sax': 1.2.7 + arg: 5.0.2 + sax: 1.6.0 + + smol-toml@1.6.1: {} + + source-map-js@1.2.1: {} + + source-map@0.7.6: {} + + space-separated-tokens@2.0.2: {} + + starlight-ion-theme@2.4.0(@astrojs/starlight@0.39.2(astro@6.4.2(@types/node@24.12.4)(rollup@4.60.4)))(astro@6.4.2(@types/node@24.12.4)(rollup@4.60.4)): + dependencies: + '@astrojs/starlight': 0.39.2(astro@6.4.2(@types/node@24.12.4)(rollup@4.60.4)) + '@iconify/tools': 4.2.0 + '@iconify/utils': 2.3.0 + '@shikijs/themes': 1.29.2 + astro: 6.4.2(@types/node@24.12.4)(rollup@4.60.4) + astro-icon: 1.1.5 + pathe: 2.0.3 + transitivePeerDependencies: + - supports-color + + stream-replace-string@2.0.0: {} + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + style-to-js@1.1.21: + dependencies: + style-to-object: 1.0.14 + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + + svgo@3.3.3: + dependencies: + commander: 7.2.0 + css-select: 5.2.2 + css-tree: 2.3.1 + css-what: 6.2.2 + csso: 5.0.5 + picocolors: 1.1.1 + sax: 1.6.0 + + svgo@4.0.1: + dependencies: + commander: 11.1.0 + css-select: 5.2.2 + css-tree: 3.2.1 + css-what: 6.2.2 + csso: 5.0.5 + picocolors: 1.1.1 + sax: 1.6.0 + + tar@7.5.15: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 + + tiny-inflate@1.0.3: {} + + tinyclip@0.1.13: {} + + tinyexec@1.2.3: {} + + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + trim-lines@3.0.1: {} + + trough@2.2.0: {} + + tslib@2.8.1: + optional: true + + ufo@1.6.4: {} + + ultrahtml@1.6.0: {} + + uncrypto@0.1.3: {} + + undici-types@7.16.0: {} + + undici@7.26.0: {} + + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unifont@0.7.4: + dependencies: + css-tree: 3.2.1 + ofetch: 1.5.1 + ohash: 2.0.11 + + unist-util-find-after@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-modify-children@4.0.0: + dependencies: + '@types/unist': 3.0.3 + array-iterate: 2.0.1 + + unist-util-position-from-estree@2.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-remove-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-visit: 5.1.0 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-children@3.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + unstorage@1.17.5: + dependencies: + anymatch: 3.1.3 + chokidar: 5.0.0 + destr: 2.0.5 + h3: 1.15.11 + lru-cache: 11.5.1 + node-fetch-native: 1.6.7 + ofetch: 1.5.1 + ufo: 1.6.4 + + util-deprecate@1.0.2: {} + + vfile-location@5.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile: 6.0.3 + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + vite@7.3.3(@types/node@24.12.4): + dependencies: + esbuild: 0.27.7 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.15 + rollup: 4.60.4 + tinyglobby: 0.2.16 + optionalDependencies: + '@types/node': 24.12.4 + fsevents: 2.3.3 + + vitefu@1.1.3(vite@7.3.3(@types/node@24.12.4)): + optionalDependencies: + vite: 7.3.3(@types/node@24.12.4) + + web-namespaces@2.0.1: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + which-pm-runs@1.1.0: {} + + wrappy@1.0.2: {} + + xxhash-wasm@1.1.0: {} + + yallist@5.0.0: {} + + yargs-parser@22.0.0: {} + + yauzl@2.10.0: + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 + + yocto-queue@1.2.2: {} + + zod@4.4.3: {} + + zwitch@2.0.4: {} diff --git a/docs/public/sql-file.png b/docs/public/sql-file.png new file mode 100644 index 0000000..74b9350 Binary files /dev/null and b/docs/public/sql-file.png differ diff --git a/docs/src/assets/sql-file.png b/docs/src/assets/sql-file.png new file mode 100644 index 0000000..74b9350 Binary files /dev/null and b/docs/src/assets/sql-file.png differ diff --git a/docs/src/content.config.ts b/docs/src/content.config.ts new file mode 100644 index 0000000..d9ee8c9 --- /dev/null +++ b/docs/src/content.config.ts @@ -0,0 +1,7 @@ +import { defineCollection } from 'astro:content'; +import { docsLoader } from '@astrojs/starlight/loaders'; +import { docsSchema } from '@astrojs/starlight/schema'; + +export const collections = { + docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), +}; diff --git a/docs/src/content/docs/cookbook.md b/docs/src/content/docs/cookbook.md new file mode 100644 index 0000000..b04d291 --- /dev/null +++ b/docs/src/content/docs/cookbook.md @@ -0,0 +1,11 @@ +--- +title: Cookbook +description: Task-focused recipes for common goway scenarios. +--- + +The recipes in this section walk through solutions to common migration tasks. Pick the one that matches your use case and adapt it to your needs. + +- **[Embedding Migrations](/goway/cookbook/embedding-migrations/)** — Package SQL scripts into your binary using `go:embed` and the `FS` method. +- **[Programmatic Setup](/goway/cookbook/programmatic-setup/)** — Initialize and run migrations from Go code with custom callbacks and configurations. +- **[Non-Transactional Migrations](/goway/cookbook/non-transactional-migrations/)** — Run operations like `CREATE INDEX CONCURRENTLY` outside a transaction. +- **[Baselining an Existing Database](/goway/cookbook/baseline-existing-database/)** — Bring an existing schema under version control by establishing a baseline version. diff --git a/docs/src/content/docs/cookbook/baseline-existing-database.md b/docs/src/content/docs/cookbook/baseline-existing-database.md new file mode 100644 index 0000000..3fd2fb8 --- /dev/null +++ b/docs/src/content/docs/cookbook/baseline-existing-database.md @@ -0,0 +1,147 @@ +--- +title: Baselining an Existing Database +description: Bring a database that already has a schema under goway management. +--- + +When you adopt goway on a database that already has a schema in place, you have two options: run every historical migration from the beginning (which may be slow or risky), or establish a baseline entry at a chosen version and skip past the history. + +## What is a baseline? + +A baseline is a marker in the schema history table that represents the current state of your database at a specific version. Once a baseline is recorded at, say, version 1, goway ignores all migrations at or below that version and applies only newer ones. This lets you bring an existing production database under migration management without replaying years of history. + +## Two approaches + +### 1. Explicit baseline (recommended for one-time adoption) + +Call `Baseline(ctx)` on your migrator to record a baseline entry with the configured version and description: + +```go +package main + +import ( + "context" + "database/sql" + "log" + + "github.com/cgardev/goway" + _ "github.com/jackc/pgx/v5/stdlib" +) + +func main() { + db, err := sql.Open("pgx", "postgresql://user:pass@localhost/mydb") + if err != nil { + log.Fatal(err) + } + defer db.Close() + + migrator, err := goway.Configure(). + DataSource(db). + Load(context.Background()) + if err != nil { + log.Fatal(err) + } + + // Record a baseline at version 1 + result, err := migrator.Baseline(context.Background()) + if err != nil { + log.Fatal(err) + } + + log.Printf("Baseline recorded: version %s, description: %s", + result.BaselineVersion, result.BaselineDescription) +} +``` + +After baselining, you may apply migrations at versions 2 and above—or configure a different baseline version if your database is already at a higher evolutionary stage: + +```go +migrator, err := goway.Configure(). + DataSource(db). + BaselineVersion("5"). + BaselineDescription("Adoption on 2026-05-31"). + Load(context.Background()) +``` + +### 2. Automatic baseline (convenient for first-run adoption) + +Set `BaselineOnMigrate(true)` to automatically record a baseline the first time `Migrate(ctx)` runs on a schema that has no history table yet: + +```go +migrator, err := goway.Configure(). + DataSource(db). + BaselineOnMigrate(true). + Load(context.Background()) + +// On the first migrate call, if the schema exists but the history table +// does not, a baseline entry is inserted automatically. +result, err := migrator.Migrate(context.Background()) +``` + +This is useful in CI/CD when you want the same code path to work both on fresh databases (which skip migrations below the baseline) and on databases being adopted for the first time. + +:::note +`BaselineOnMigrate` only triggers if the schema has content but no history table. If a history table already exists, even with failed or pending migrations, `Migrate` proceeds normally without baselining. +::: + +## Baseline defaults + +If you do not configure baseline settings, goway uses these defaults: + +- **BaselineVersion**: `"1"` +- **BaselineDescription**: `"<< Flyway Baseline >>"` + +These match Flyway's defaults, ensuring compatibility if you ever migrate a schema between tools. + +## Schema history requirements + +`Baseline` requires that either: +- The schema history table exists, or +- The target schema exists and can have the table created in it (controlled by `CreateSchemas`) + +If the history table does not exist, `Baseline` creates it. If it does exist but contains applied migrations, `Baseline` returns an error—you cannot baseline a schema that has already run migrations. + +## Existing Flyway integration + +If your database already has a `flyway_schema_history` table from Flyway, goway can adopt it directly. Point goway to the same table name and database, and it will respect the existing history: + +```go +migrator, err := goway.Configure(). + DataSource(db). + Table("flyway_schema_history"). // The table name Flyway created + Load(context.Background()) + +result, err := migrator.Migrate(context.Background()) +``` + +goway recognizes and preserves Flyway baseline entries and will skip migrations at or below the recorded baseline version, just as Flyway does. New migrations you apply with goway will follow Flyway's naming scheme and history schema. + +:::tip +When migrating from Flyway to goway on an existing database, no changes to the history table are needed. goway uses the same defaults for table name, column schema, and migration naming conventions. +::: + +## CLI adoption + +From the command line, baseline an existing database with: + +```sh +goway -url postgresql://user:pass@localhost/mydb \ + -baseline-version 1 \ + -baseline-description "Adoption on 2026-05-31" \ + baseline +``` + +Then apply new migrations normally: + +```sh +goway -url postgresql://user:pass@localhost/mydb migrate +``` + +Or enable automatic baselining so the first `migrate` command does both: + +```sh +goway -url postgresql://user:pass@localhost/mydb \ + -baseline-on-migrate \ + migrate +``` + +See [Commands](/goway/reference/commands/) for the complete CLI reference. diff --git a/docs/src/content/docs/cookbook/embedding-migrations.md b/docs/src/content/docs/cookbook/embedding-migrations.md new file mode 100644 index 0000000..d1f546b --- /dev/null +++ b/docs/src/content/docs/cookbook/embedding-migrations.md @@ -0,0 +1,162 @@ +--- +title: Embedding Migrations +description: Compile your migration scripts into your binary with go:embed. +--- + +When you compile your application, you can embed your migration scripts directly into the binary using Go's `go:embed` directive. This produces a single, self-contained executable that carries everything needed to initialize and migrate the database—no separate migration files to ship, no file paths to configure on disk. + +## Why Embed Migrations? + +Embedding migrations is particularly useful when deploying applications as a single binary: + +- **Single artifact**: Deploy one executable; no need to manage migration files in version control or copy them to production. +- **Atomicity**: The migrations and application code always stay in sync. +- **Portability**: The binary works the same way on any platform or container, without depending on filesystem paths at runtime. +- **Easier testing**: Use the embedded migrations in unit or integration tests without mocking the file system. + +## How It Works + +Use Go's [`go:embed` directive](https://pkg.go.dev/embed) to embed a directory of SQL scripts into an `embed.FS`, then pass that filesystem to the migrator via the `.FS()` method: + +```go +import ( + "embed" + "github.com/cgardev/goway" +) + +//go:embed migrations/*.sql +var migrations embed.FS + +migrator, err := goway.Configure(). + DataSource(db). + FS(migrations, "migrations"). + Load() +``` + +The `.FS()` method accepts an `embed.FS` and one or more directory paths within it. If no paths are specified, it defaults to `"."` (the root of the filesystem). + +You can call `.FS()` multiple times to register migrations from different embedded directories, just as you can chain multiple `.Locations()` calls for filesystem paths. Migrations are discovered and sorted across all sources. + +## Example: SQLite with Embedded Migrations + +Here is a complete, runnable example that embeds SQLite migrations and applies them in-process: + +### Directory Layout + +``` +myapp/ +├── main.go +├── go.mod +└── migrations/ + ├── V1__init.sql + ├── V2__add_users_table.sql + └── V3__add_index.sql +``` + +### Code + +```go +package main + +import ( + "context" + "database/sql" + "embed" + "fmt" + "log" + "os" + "path/filepath" + + "github.com/cgardev/goway" + + _ "modernc.org/sqlite" +) + +//go:embed migrations/*.sql +var migrations embed.FS + +func main() { + if err := run(); err != nil { + log.Fatal(err) + } +} + +func run() error { + // Create or open a database (for this example, a temporary file). + dbPath := filepath.Join(os.TempDir(), "myapp.db") + db, err := sql.Open("sqlite", dbPath) + if err != nil { + return err + } + defer db.Close() + + // Configure the migrator with embedded migrations. + migrator, err := goway.Configure(). + DataSource(db). + Dialect(goway.SQLite()). + FS(migrations, "migrations"). + Load() + if err != nil { + return err + } + + // Run migrations. + ctx := context.Background() + result, err := migrator.Migrate(ctx) + if err != nil { + return err + } + + fmt.Printf("Applied %d migration(s); schema is now at version %s.\n", + result.MigrationsExecuted, result.TargetSchemaVersion) + + return nil +} +``` + +### Example Migrations + +**migrations/V1__init.sql:** +```sql +CREATE TABLE products ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL +); +``` + +**migrations/V2__add_users_table.sql:** +```sql +CREATE TABLE users ( + id INTEGER PRIMARY KEY, + email TEXT UNIQUE NOT NULL +); +``` + +**migrations/V3__add_index.sql:** +```sql +CREATE INDEX idx_users_email ON users(email); +``` + +## Combining Embedded and Filesystem Locations + +You can mix embedded and filesystem sources: + +```go +migrator, err := goway.Configure(). + DataSource(db). + Locations("filesystem:db/migrations"). // scan the filesystem + FS(embedded, "migrations"). // also scan the embedded FS + Load() +``` + +Migrations are resolved from both locations and sorted together, so you can keep some migrations on disk while others are embedded. + +:::note +The example module in the repository (`example/main.go`) demonstrates this exact pattern with SQLite. Run `go run ./example` to see it in action. +::: + +## See Also + +- [Commands](/goway/reference/commands/) — `migrate`, `info`, `validate`, and other operations +- [Configuration Reference](/goway/reference/configuration/) — all available configuration options +- [Writing Migrations](/goway/guides/writing-migrations/) — naming conventions and script syntax diff --git a/docs/src/content/docs/cookbook/non-transactional-migrations.md b/docs/src/content/docs/cookbook/non-transactional-migrations.md new file mode 100644 index 0000000..bd3da67 --- /dev/null +++ b/docs/src/content/docs/cookbook/non-transactional-migrations.md @@ -0,0 +1,112 @@ +--- +title: Non-Transactional Migrations +description: Run statements like CREATE INDEX CONCURRENTLY or VACUUM that cannot run in a transaction. +--- + +Some SQL statements cannot execute inside a transaction block. PostgreSQL's `CREATE INDEX CONCURRENTLY` allows index creation without locking the table for writes, but requires running outside a transaction. SQLite's `VACUUM` command also demands a transaction-free context. goway handles these with the `goway:noTransaction` directive. + +## Marking a Migration + +Add a comment line to the top of your migration file containing `goway:noTransaction` (case-insensitive): + +```sql +-- goway:noTransaction + +CREATE INDEX CONCURRENTLY idx_users_email ON users(email); +``` + +The `-- flyway:executeInTransaction=false` form is also accepted for compatibility with Flyway: + +```sql +-- flyway:executeInTransaction=false + +VACUUM; +``` + +Detection is case-insensitive and looks only at comment lines, so a marker inside a string literal or statement has no effect. The directive must appear before any SQL statements. + +## Execution Model + +When goway encounters a non-transactional migration, it: + +1. Reads and parses the script normally +2. Acquires a dedicated database connection (not pooled from the normal transaction) +3. Runs all statements on that connection **outside a transaction block** +4. Records the result in the schema history table + +This means statements execute with auto-commit enabled, without the safety of rollback. + +## Failure Handling + +If a non-transactional migration fails, goway records a failed history row. Unlike transactional migrations (which roll back automatically on error), the non-transactional statement may have partially succeeded. Fix the script and then run [`Repair()`](/goway/reference/commands/) to clear the failed entry before retrying: + +```go +// After fixing the migration file: +result, err := migrator.Repair(ctx) +if err != nil { + log.Fatal(err) +} +// RemovedFailed migrations can now be retried +result, err = migrator.Migrate(ctx) +``` + +:::caution +Non-transactional migrations have no automatic rollback. Ensure your script is correct before executing it, and test thoroughly on a replica or staging database. +::: + +## PostgreSQL: Concurrent Indexes + +Create indexes without blocking writes: + +```sql +-- goway:noTransaction + +-- Create the index in the background without blocking INSERT/UPDATE/DELETE on the table +CREATE INDEX CONCURRENTLY idx_users_created_at ON users(created_at); + +-- Optionally, run validation or maintenance in the same migration +ANALYZE users; +``` + +## SQLite: Vacuum and Optimization + +Reclaim disk space or rebuild the entire database: + +```sql +-- goway:noTransaction + +-- Rebuild the database file and reclaim unused space +VACUUM; + +-- Optionally optimize the query planner statistics +ANALYZE; +``` + +:::note +SQLite also supports incremental vacuum (`PRAGMA incremental_vacuum`) inside a transaction, if you want a less disruptive alternative to full vacuum. +::: + +## Search Path and Schema Context + +On PostgreSQL, goway sets the `search_path` session variable per migration using `SET` rather than a transaction-based approach, ensuring your unqualified objects are placed in the correct schema even outside a transaction. + +On SQLite, schema context has no equivalent; goway operates on the default database. + +## Placeholders + +Placeholder replacement happens at execution time and does not affect the checksum calculation. Non-transactional migrations can use placeholders just like any other migration: + +```sql +-- goway:noTransaction + +CREATE INDEX CONCURRENTLY idx_on_${flyway:defaultSchema}_users_email + ON users(email); +``` + +See [Placeholders](/goway/reference/placeholders/) for details. + +## When to Use + +- **PostgreSQL**: `CREATE INDEX CONCURRENTLY`, `REINDEX CONCURRENTLY`, `CLUSTER`, or any statement that explicitly forbids transactions +- **SQLite**: `VACUUM`, `PRAGMA optimize`, or other pragma statements that require auto-commit +- **Avoid**: Regular table creation, data migration, or DML statements—these should use transactional migrations for safety diff --git a/docs/src/content/docs/cookbook/programmatic-setup.md b/docs/src/content/docs/cookbook/programmatic-setup.md new file mode 100644 index 0000000..8312dbb --- /dev/null +++ b/docs/src/content/docs/cookbook/programmatic-setup.md @@ -0,0 +1,278 @@ +--- +title: Programmatic Setup +description: Run migrations at application startup, including per-module schemas. +--- + +When you need goway to run migrations as part of your application's initialization—rather than as a separate CLI step—integrate it directly into your startup code. This is common in microservices, modular monoliths, and any server that manages its own schema lifecycle. + +## Basic Setup + +The simplest pattern is to open your database connection and then call `Configure()`, chain the necessary setters, call `Load()`, and finally call `Migrate()` before your app starts serving requests. + +```go +package main + +import ( + "context" + "database/sql" + "log" + + "github.com/cgardev/goway" + _ "github.com/jackc/pgx/v5/stdlib" +) + +func main() { + // Open the connection pool + db, err := sql.Open("pgx", "postgresql://user:pass@localhost/mydb") + if err != nil { + log.Fatalf("opening database: %v", err) + } + defer db.Close() + + // Configure and run migrations + migrator, err := goway.Configure(). + DataSource(db). + Locations("db/migrations"). + Load() + if err != nil { + log.Fatalf("loading migrations: %v", err) + } + + result, err := migrator.Migrate(context.Background()) + if err != nil { + log.Fatalf("migrating: %v", err) + } + + log.Printf("migrated to version %s (%d migrations applied)", + result.TargetSchemaVersion, result.MigrationsExecuted) + + // Now start your application + startServer() +} +``` + +## Multiple Modules with Per-Schema Setup + +In a larger application, you may have multiple features or services, each with its own schema and migration location. Run migrations for each module in sequence: + +```go +package main + +import ( + "context" + "database/sql" + "log" + + "github.com/cgardev/goway" + _ "github.com/jackc/pgx/v5/stdlib" +) + +func migrateModule(db *sql.DB, schema, location string) error { + migrator, err := goway.Configure(). + DataSource(db). + Schemas(schema). + Locations(location). + Load() + if err != nil { + return err + } + + result, err := migrator.Migrate(context.Background()) + if err != nil { + return err + } + + log.Printf("%s: version %s (%d migrations)", + schema, result.TargetSchemaVersion, result.MigrationsExecuted) + return nil +} + +func main() { + db, err := sql.Open("pgx", "postgresql://user:pass@localhost/platform") + if err != nil { + log.Fatalf("opening database: %v", err) + } + defer db.Close() + + // Migrate each module's schema in order + modules := []struct { + schema string + location string + }{ + {"users", "modules/users/migrations"}, + {"auth", "modules/auth/migrations"}, + {"billing", "modules/billing/migrations"}, + } + + for _, m := range modules { + if err := migrateModule(db, m.schema, m.location); err != nil { + log.Fatalf("migrating %s: %v", m.schema, err) + } + } + + startServer() +} +``` + +:::note +When you pass a schema to `Schemas()`, goway automatically creates it if it does not exist (controlled by `CreateSchemas()`, which defaults to true). The schema history table is created in that schema. +::: + +## Understanding CreateSchemas and DefaultSchema + +**CreateSchemas** controls whether goway automatically creates missing schemas. It defaults to `true`—a safe choice for application startup where you want the schema to exist before your application runs. + +**DefaultSchema** explicitly specifies which schema holds the schema history table. If not set, goway uses: +1. The first schema passed to `Schemas()`, if any +2. The database's current schema (e.g., `public` on PostgreSQL) + +In most cases, you do not need to set `DefaultSchema`. Use `Schemas()` to declare which schemas the migrator manages, and the first one becomes the default: + +```go +// Schema history lives in "core"; migrations also create "users" and "orders" +goway.Configure(). + DataSource(db). + Schemas("core", "users", "orders"). + Locations("db/migrations"). + Load() +``` + +If you want the history table in a different schema than the one in `Schemas()`, set it explicitly: + +```go +// History table is in "system"; migrations manage "app" and "analytics" +goway.Configure(). + DataSource(db). + Schemas("app", "analytics"). + DefaultSchema("system"). + Locations("db/migrations"). + Load() +``` + +## Structured Helper + +For cleaner, more testable startup code, wrap the configuration into a helper function: + +```go +package database + +import ( + "context" + "database/sql" + "fmt" + + "github.com/cgardev/goway" +) + +// RunMigrations runs all pending migrations and returns the outcome. +// If migration fails, the error is returned and the application should not start. +func RunMigrations(ctx context.Context, db *sql.DB, config MigrationConfig) error { + migrator, err := goway.Configure(). + DataSource(db). + Schemas(config.Schemas...). + DefaultSchema(config.DefaultSchema). + Locations(config.Locations...). + CreateSchemas(config.CreateSchemas). + ValidateOnMigrate(config.ValidateOnMigrate). + Load() + if err != nil { + return fmt.Errorf("loading migrations: %w", err) + } + + result, err := migrator.Migrate(ctx) + if err != nil { + return fmt.Errorf("migrate: %w", err) + } + + if result.MigrationsExecuted > 0 { + fmt.Printf("Applied %d migrations, now at version %s\n", + result.MigrationsExecuted, result.TargetSchemaVersion) + } else { + fmt.Println("Schema is up to date") + } + return nil +} + +// MigrationConfig holds the settings for a migration run. +type MigrationConfig struct { + Schemas []string // Required: schemas to manage + DefaultSchema string // Optional: where the history table lives + Locations []string // Optional: defaults to ["db/migration"] + CreateSchemas bool // Optional: defaults to true + ValidateOnMigrate bool // Optional: defaults to true +} +``` + +Then in your `main()`: + +```go +func main() { + db, err := sql.Open("pgx", "postgresql://user:pass@localhost/mydb") + if err != nil { + log.Fatalf("opening database: %v", err) + } + defer db.Close() + + err = database.RunMigrations(context.Background(), db, database.MigrationConfig{ + Schemas: []string{"public"}, + Locations: []string{"db/migrations"}, + }) + if err != nil { + log.Fatalf("migrations failed: %v", err) + } + + startServer() +} +``` + +## Error Handling + +Migrations can fail for several reasons: invalid SQL, checksum mismatches, missing files, or schema creation issues. Always treat migration errors as fatal and halt startup: + +```go +result, err := migrator.Migrate(ctx) +if err != nil { + if errors.Is(err, goway.ErrValidationFailed) { + // A migration's checksum does not match its history record + log.Fatalf("schema is inconsistent; run goway repair: %v", err) + } + if errors.Is(err, goway.ErrFailedMigration) { + // A previous migration failed and is still in the history + log.Fatalf("failed migration must be repaired before proceeding: %v", err) + } + log.Fatalf("migration failed: %v", err) +} +``` + +See the [Commands](/goway/reference/commands/) page for recovery strategies using `Validate()` and `Repair()`. + +## Transactions and Isolation + +Each migration runs inside its own transaction by default, so a failure rolls back all its changes and leaves the database in a consistent state. The migration can then be corrected and retried on the next startup. + +Some statements (such as PostgreSQL's `CREATE INDEX CONCURRENTLY` or SQLite's `VACUUM`) cannot run inside a transaction. Mark these migrations with a comment at the top of the script: + +```sql +-- goway:noTransaction + +CREATE INDEX CONCURRENTLY idx_users_email ON users(email); +``` + +When a non-transactional migration fails, a failed history entry is still recorded so that you can inspect and repair it with the `Repair` command. + +:::caution +Avoid mixing transactional and non-transactional statements in a single script. Separate them into distinct migration files. +::: + +## Context and Timeout + +Pass a context with a suitable timeout to `Migrate()` to prevent migrations from hanging indefinitely on a slow database or network: + +```go +ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) +defer cancel() + +result, err := migrator.Migrate(ctx) +``` + +On context cancellation or timeout, any in-flight migration is rolled back (if it was transactional) and an error is returned. diff --git a/docs/src/content/docs/getting-started.md b/docs/src/content/docs/getting-started.md new file mode 100644 index 0000000..5a24b8f --- /dev/null +++ b/docs/src/content/docs/getting-started.md @@ -0,0 +1,163 @@ +--- +title: Getting Started +description: Install goway, write your first migration, and run it against PostgreSQL or SQLite. +--- + +## Installation + +Add goway to your project: + +```sh +go get github.com/cgardev/goway +``` + +The core library depends only on the standard library. You supply the database driver separately. + +## Choose a driver + +goway supports PostgreSQL and SQLite. Each has its own driver, and you choose the one your application already owns. + +**PostgreSQL** via pgx (github.com/jackc/pgx/v5): + +```go +import _ "github.com/jackc/pgx/v5/stdlib" +// Then: sql.Open("pgx", "postgres://user:password@localhost:5432/dbname") +``` + +**SQLite** via modernc.org/sqlite (pure Go, no C dependencies): + +```go +import _ "modernc.org/sqlite" +// Then: sql.Open("sqlite", "path/to/database.db") +``` + +Why does goway not depend on a driver? Because your application may already use a driver for other purposes, and you should never import two driver implementations. goway stays small and lets your application decide. + +## Write your first migration + +Create a directory for migrations: + +```sh +mkdir -p db/migration +``` + +Inside, create a versioned migration file. The naming convention is `V__.sql`. The version can be a single number or dot-separated (e.g., `1`, `1.0`, `2.1`). The description is any slug; underscores become spaces. + +Create `db/migration/V1__create_users.sql`: + +```sql +CREATE TABLE users ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL +); +``` + +Create `db/migration/V2__add_email_to_users.sql`: + +```sql +ALTER TABLE users ADD COLUMN email TEXT; + +CREATE UNIQUE INDEX idx_users_email ON users (email); +``` + +## Run migrations + +Here is a complete, runnable example for SQLite. It opens an in-memory database, applies the migrations, and prints the result: + +```go +package main + +import ( + "context" + "database/sql" + "fmt" + "log" + + "github.com/cgardev/goway" + + _ "modernc.org/sqlite" +) + +func main() { + // Open the database + db, err := sql.Open("sqlite", ":memory:") + if err != nil { + log.Fatal(err) + } + defer db.Close() + + // Configure the migrator + migrator, err := goway.Configure(). + DataSource(db). + Locations("filesystem:db/migration"). + Load() + if err != nil { + log.Fatal(err) + } + + // Run migrations + ctx := context.Background() + result, err := migrator.Migrate(ctx) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Applied %d migration(s)\n", result.MigrationsExecuted) + fmt.Printf("Schema is now at version %s\n", result.TargetSchemaVersion) +} +``` + +For **PostgreSQL**, change only the database opening: + +```go +import _ "github.com/jackc/pgx/v5/stdlib" + +func main() { + db, err := sql.Open("pgx", "postgres://user:password@localhost:5432/app") + // ... rest is identical +} +``` + +## Understanding the result + +`Migrate` returns a `*MigrateResult` with: + +- **InitialSchemaVersion** — the version before the run +- **TargetSchemaVersion** — the version after the run +- **MigrationsExecuted** — the number of migrations applied +- **Migrations** — a slice of `MigrationInfo` for each migration that ran, containing the version, description, script name, and execution time + +If any migration fails, `Migrate` returns an error and stops. No partial state is left; the transaction containing that migration is rolled back. + +## Check migration state + +Use `Info` to see all migrations and their status without making changes: + +```go +information, err := migrator.Info(ctx) +if err != nil { + log.Fatal(err) +} + +for _, migration := range information.Migrations { + fmt.Printf("%s: %s\n", migration.Version, migration.State) +} +``` + +The `State` field tells you whether a migration is "Pending", "Success", "Failed", or another status. The `Current` field on `InfoResult` gives the highest version that has been applied. + +## What happens under the hood + +- **Auto-detection**: The dialect (PostgreSQL or SQLite) is detected automatically from the connection unless you call `.Dialect()` explicitly. +- **Schema history table**: A table named `flyway_schema_history` is created in the default schema. It records every migration: when it ran, by whom, its checksum, and whether it succeeded. +- **Checksums**: Each migration file is checksummed (CRC32) at load time. If the file changes after being applied, validation will catch it. +- **Transactions**: Each migration runs inside its own transaction, so a failure leaves no partial state. +- **Defaults**: Migrations are discovered in `db/migration` by default; the schema history table is created automatically; schemas are created if they do not exist. + +See [Configuration](/goway/reference/configuration/) for all settable options. + +## Next steps + +- [Writing Migrations](/goway/guides/writing-migrations/) — dive deeper into migration naming, repeatable migrations, and placeholders +- [Commands](/goway/reference/commands/) — migrate, info, validate, baseline, repair, clean +- [Configuration](/goway/reference/configuration/) — all fluent configuration methods and their defaults diff --git a/docs/src/content/docs/guides/callbacks.md b/docs/src/content/docs/guides/callbacks.md new file mode 100644 index 0000000..254f0f3 --- /dev/null +++ b/docs/src/content/docs/guides/callbacks.md @@ -0,0 +1,152 @@ +--- +title: Callbacks +description: Run logic before and after migrations using SQL scripts or Go callbacks. +--- + +Callbacks let you hook into the migration lifecycle to run custom logic at key points. You can run SQL statements via callback scripts discovered by filename, or register programmatic callbacks in Go to execute arbitrary logic. + +## Lifecycle Events + +There are four callback events, fired at specific points during a `Migrate` run: + +- **beforeMigrate**: fires once before any migration is applied +- **afterMigrate**: fires once after all migrations have been applied +- **beforeEachMigrate**: fires before each individual migration +- **afterEachMigrate**: fires after each individual migration + +The per-migration events (`beforeEachMigrate`, `afterEachMigrate`) run on the same transaction or executor as the migration itself, so they can participate in the migration's atomicity. The run-level events (`beforeMigrate`, `afterMigrate`) receive the database connection pool. + +## SQL Callback Scripts + +Callback scripts are SQL files discovered in the configured `Locations` by their filename. A callback filename must match an event name, optionally followed by the `SQLMigrationSeparator` and a description. + +### Naming + +The format is `[__].sql`, where `` is case-insensitive: + +- `beforeMigrate.sql` — runs before any migration +- `afterMigrate.sql` — runs after all migrations +- `beforeEachMigrate.sql` — runs before each migration +- `beforeEachMigrate__pre_checks.sql` — before each migration, with a description +- `afterEachMigrate__cleanup.sql` — after each migration, with a description + +The separator is configurable via `Configure().SQLMigrationSeparator(...)` (default: `"__"`). + +:::tip +A callback script can contain multiple SQL statements. Like migrations, statements are split on statement delimiters appropriate to your dialect (semicolons in PostgreSQL; also understanding SQLite's `BEGIN`, `CASE`, and `END` blocks). +::: + +### Placeholders + +Callback scripts support [placeholders](/goway/reference/placeholders/) just like migrations. Built-in placeholders like `${flyway:defaultSchema}` and `${flyway:user}` are always available. + +## Programmatic Callbacks + +For complex logic, register callbacks in Go by implementing the `Callback` interface or using the `CallbackFunc` adapter: + +```go +type Callback interface { + Handle(ctx context.Context, event CallbackEvent, exec Execer, migration *MigrationInfo) error +} +``` + +- `event` identifies which lifecycle event is firing (`EventBeforeMigrate`, `EventAfterMigrate`, `EventBeforeEachMigrate`, `EventAfterEachMigrate`) +- `exec` is the executor to run SQL on: a `*sql.DB` for run-level events, or a `*sql.Tx` for per-migration events +- `migration` describes the migration being processed; it is `nil` for run-level events + +The `Execer` interface is minimal — it is satisfied by `*sql.DB`, `*sql.Tx`, and `*sql.Conn`: + +```go +type Execer interface { + ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) +} +``` + +### CallbackFunc Adapter + +`CallbackFunc` is a function type that adapts a closure to the `Callback` interface: + +```go +type CallbackFunc func(ctx context.Context, event CallbackEvent, exec Execer, migration *MigrationInfo) error +``` + +This lets you pass a function directly to `Configure().Callbacks()` without defining a type. + +### Example + +```go +package main + +import ( + "context" + "database/sql" + "fmt" + "github.com/cgardev/goway" + _ "github.com/jackc/pgx/v5/stdlib" +) + +func main() { + db, _ := sql.Open("pgx", "postgresql://localhost/mydb") + defer db.Close() + + beforeEach := goway.CallbackFunc(func(ctx context.Context, event goway.CallbackEvent, exec goway.Execer, migration *goway.MigrationInfo) error { + if event != goway.EventBeforeEachMigrate { + return nil + } + fmt.Printf("Applying %s: %s\n", migration.Version, migration.Description) + return nil + }) + + afterEach := goway.CallbackFunc(func(ctx context.Context, event goway.CallbackEvent, exec goway.Execer, migration *goway.MigrationInfo) error { + if event != goway.EventAfterEachMigrate { + return nil + } + fmt.Printf("Applied %s in %dms\n", migration.Version, migration.ExecutionTime) + return nil + }) + + migrator, _ := goway.Configure(). + DataSource(db). + Locations("db/migrations"). + Callbacks(beforeEach, afterEach). + Load() + + result, _ := migrator.Migrate(context.Background()) + fmt.Printf("Executed %d migrations\n", result.MigrationsExecuted) +} +``` + +## Order of Execution + +During `Migrate`, callbacks fire in this order: + +1. Run-level `beforeMigrate` callbacks (SQL scripts, then programmatic) +2. For each pending migration: + - Per-migration `beforeEachMigrate` callbacks (SQL scripts, then programmatic) + - Migration statements + - Per-migration `afterEachMigrate` callbacks (SQL scripts, then programmatic) + - History record inserted +3. Run-level `afterMigrate` callbacks (SQL scripts, then programmatic) + +SQL callback scripts are executed first, followed by programmatic callbacks registered via `Callbacks()`. + +## Transaction Scope + +- **Run-level events** (`beforeMigrate`, `afterMigrate`) receive the database connection pool (`*sql.DB`) and can manage their own transactions. +- **Per-migration events** (`beforeEachMigrate`, `afterEachMigrate`) receive the same transaction as the migration (when transactional), so they participate in the migration's atomicity. If a callback returns an error, the transaction rolls back and the migration fails. +- **Non-transactional migrations** (those with `-- goway:noTransaction` or `-- flyway:executeInTransaction=false`) receive a dedicated connection instead. + +:::caution +If a callback in a per-migration event returns an error, the entire migration is rolled back and marked failed in the history. The error is returned to the caller. +::: + +## Error Handling + +If any callback returns an error, the migration run stops immediately and the error is returned to the caller. The history table records the state of migrations executed up to that point. + +For SQL callback scripts, if a statement fails to parse or execute, the error includes the callback filename and is wrapped with context. + +## See Also + +- [Callbacks Reference](/goway/reference/callbacks/) for the complete type documentation +- [Writing Migrations](/goway/guides/writing-migrations/) for migration syntax and non-transactional migrations diff --git a/docs/src/content/docs/guides/command-line.md b/docs/src/content/docs/guides/command-line.md new file mode 100644 index 0000000..1db7c9d --- /dev/null +++ b/docs/src/content/docs/guides/command-line.md @@ -0,0 +1,293 @@ +--- +title: Command Line +description: Run migrations from the goway CLI instead of from your application. +--- + +The `goway` CLI provides a command-line interface to run migrations without embedding the tool in your application. This is useful for standalone deployments, CI/CD pipelines, and development workflows. + +## Installation + +Install the CLI by running it directly with `go run`: + +```sh +go run github.com/cgardev/goway/cmd/goway -help +``` + +Or build it into a binary: + +```sh +go build github.com/cgardev/goway/cmd/goway +./goway -help +``` + +## Database Connection + +Every command requires a database connection specified via the `-url` flag or the `GOWAY_URL` environment variable. If both are set, `-url` takes precedence. + +### URL Schemes + +**PostgreSQL:** +- `postgres://user:password@host:5432/database` +- `postgresql://user:password@host:5432/database` + +Both schemes are supported. The driver is [`github.com/jackc/pgx/v5`](https://github.com/jackc/pgx). + +**SQLite:** +- `sqlite:./path/to/database.db` +- `file:./path/to/database.db` + +Both schemes work with the pure-Go [`modernc.org/sqlite`](https://modernc.org/sqlite) driver. Relative paths are resolved from the current working directory. + +### Example + +```sh +# Using a flag +goway -url "postgres://localhost/mydb" migrate + +# Using an environment variable +export GOWAY_URL="sqlite:./app.db" +goway migrate +``` + +## Common Flags + +The most frequently used flags are: + +**`-locations` (default: `filesystem:db/migration`)** +Comma-separated list of migration directories or embedded filesystem paths. Use `filesystem:` prefix for filesystem locations. + +```sh +goway -url postgres://localhost/mydb \ + -locations "filesystem:sql/migrations, filesystem:sql/repeatable" \ + migrate +``` + +**`-schemas`** +Comma-separated list of schemas to use. The first schema is the default. Relevant for PostgreSQL; ignored for SQLite. + +```sh +goway -url postgres://localhost/mydb \ + -schemas "public, audit" \ + migrate +``` + +**`-table` (default: `flyway_schema_history`)** +Name of the migration history table. + +```sh +goway -url postgres://localhost/mydb \ + -table "schema_migrations" \ + migrate +``` + +**`-create-schemas` (default: `true`)** +Automatically create missing schemas on migration. + +```sh +goway -url postgres://localhost/mydb \ + -create-schemas=false \ + migrate +``` + +**`-target`** +Apply migrations only up to a specific version. Useful for rolling back to a known state (though note: goway does not support downmigrations; this stops at a particular version). + +```sh +goway -url postgres://localhost/mydb \ + -target "2.5" \ + migrate +``` + +**`-placeholders`** +Comma-separated `key=value` pairs for placeholder substitution in migration scripts. + +```sh +goway -url postgres://localhost/mydb \ + -placeholders "env=production, app_schema=app_data" \ + migrate +``` + +For a complete list of flags, run `goway -help`. See the [CLI reference](/goway/reference/cli/) for details on baseline, repair, and clean options. + +## Commands + +Every invocation takes exactly one command: + +### migrate + +Apply all pending migrations and move the schema to the target version. + +```sh +goway -url postgres://localhost/mydb migrate +``` + +Output on success: +``` +Successfully applied 3 migration(s) to version 2.1: + 1 init + 1.1 add_users_table + 2.1 add_index +``` + +If the schema is already up to date: +``` +Schema is up to date. No migration necessary. (version 2.1) +``` + +#### Baseline on Migrate + +Use `-baseline-on-migrate` to automatically baseline an existing schema on the first run, then apply new migrations. This is useful when adopting goway on a project with existing schema. + +```sh +goway -url postgres://localhost/mydb \ + -baseline-on-migrate \ + migrate +``` + +:::note +This flag is a no-op if a baseline entry already exists in the history table. +::: + +### info + +Display the current migration status and history. Shows all applied, pending, and failed migrations. + +```sh +goway -url postgres://localhost/mydb info +``` + +Output: +``` +Version Description Type State Installed On +1 init SQL Success 2024-12-15 14:23:01 +1.1 add_users_table SQL Success 2024-12-15 14:23:02 +2 add_posts_table SQL Pending + cleanup SQL Pending + +Current version: 1.1 +``` + +Columns: +- **Version**: Migration version number; `` for repeatable migrations. +- **Description**: Human-readable migration name. +- **Type**: `SQL`, `BASELINE`, `SCHEMA`, or `DELETED`. +- **State**: `Success`, `Pending`, `Failed`, `Out of Order`, `Missing`, or other [migration states](/goway/reference/schema-history/). +- **Installed On**: When the migration ran; empty if pending. + +### validate + +Check the integrity of applied migrations. Validates that migration checksums match the recorded values and that there are no gaps or conflicts. + +```sh +goway -url postgres://localhost/mydb validate +``` + +Output on success: +``` +Validation successful: 5 migration(s) validated. +``` + +On failure, lists each problem: +``` +Validation failed: + 1.1: Checksum mismatch (expected 123456, got 654321) + 2.0: Missing migration (not found on disk) +``` + +:::caution +Validation is enabled by default during migrate. Use this command to validate without applying migrations. +::: + +### baseline + +Record the current schema state as a baseline without applying any migration. Use this when adopting goway on a project with existing database schema. + +```sh +goway -url postgres://localhost/mydb baseline +``` + +Output: +``` +Successfully baselined schema at version 1. +``` + +If a baseline already exists: +``` +Schema is already baselined at version 1. +``` + +Options: +- `-baseline-version` (default: `1`): Version to record. +- `-baseline-description` (default: `<< Flyway Baseline >>`): Description text. + +```sh +goway -url postgres://localhost/mydb \ + -baseline-version "2024.1" \ + -baseline-description "Legacy schema snapshot" \ + baseline +``` + +### repair + +Remove failed migration entries and re-align migration checksums. Use this after fixing a broken migration script or correcting a corrupted history entry. + +```sh +goway -url postgres://localhost/mydb repair +``` + +Output: +``` +Repair complete: removed 1 failed entry(ies), realigned 0 checksum(s). +``` + +:::caution +Repair modifies the migration history table. Use only when you are confident the database and migration files are in a consistent state. +::: + +### clean + +Delete all objects (tables, views, indexes, schemas) in the target schemas, leaving the history table intact. **Disabled by default.** + +```sh +# Enable with -clean-disabled=false +goway -url postgres://localhost/mydb \ + -clean-disabled=false \ + clean +``` + +Output: +``` +Successfully cleaned schema(s): public, audit. +``` + +:::caution +Clean is destructive and disabled by default for safety. Do not enable in production. Use only in development or test environments. +::: + +## Exit Codes + +- `0`: Command succeeded. +- `1`: Command failed (invalid URL, unsupported database, migration error, validation failure, etc.). + +## Error Messages + +Common errors and how to resolve them: + +**`a database URL is required, set -url or GOWAY_URL`** +Provide a database URL via `-url` flag or `GOWAY_URL` environment variable. + +**`unsupported database URL; use a postgres:// or sqlite: URL`** +Check the URL format. Only PostgreSQL (`postgres://`, `postgresql://`) and SQLite (`sqlite:`, `file:`) are supported. + +**`Validation failed: ... Checksum mismatch`** +A migration file has been modified after execution. Either revert the file or use `repair` to update the recorded checksum. + +**`clean-disabled is true; cannot clean`** +Pass `-clean-disabled=false` to enable the clean command. Only do this in development or test environments. + +## See Also + +- [Writing Migrations](/goway/guides/writing-migrations/) +- [Using Goway in Your Application](/goway/cookbook/embedding-migrations/) +- [Migration States](/goway/reference/schema-history/) +- [Full CLI Reference](/goway/reference/cli/) diff --git a/docs/src/content/docs/guides/configuration.md b/docs/src/content/docs/guides/configuration.md new file mode 100644 index 0000000..09e6d18 --- /dev/null +++ b/docs/src/content/docs/guides/configuration.md @@ -0,0 +1,359 @@ +--- +title: Configuration +description: Every setting on the fluent configuration builder and its default value. +--- + +Every aspect of goway's behavior is controlled through the fluent `Configuration` builder. You create one with `Configure()`, chain setter calls to customize it, then call `.Load()` (or `.LoadContext(ctx)`) to produce a ready-to-use `Migrator`. + +## Data Source and Dialect + +The database connection and its type are the foundation of every migration run. + +### DataSource + +```go +config := goway.Configure(). + DataSource(db). + Load() +``` + +Required. Pass your `*sql.DB` connection pool. There is no default. + +### Dialect + +```go +config := goway.Configure(). + DataSource(db). + Dialect(goway.Postgres()). + Load() +``` + +Optional. goway auto-detects the dialect from the connection if you do not set it. You can set it explicitly to bypass detection or to choose a specific dialect: + +- `goway.Postgres()` — PostgreSQL 15 and later +- `goway.SQLite()` — SQLite 3.37 and later + +If you do not set a dialect, `.Load()` queries the database to determine it. + +## Locations and File Systems + +Tell goway where to find migration scripts. + +### Locations + +```go +config := goway.Configure(). + DataSource(db). + Locations("db/migration", "db/util"). + Load() +``` + +Default: `["db/migration"]` + +Replaces the list of file system directories to scan. Locations are relative to the current working directory (when running a CLI) or the program's entry point (when embedded). You may include `filesystem:` or `classpath:` prefixes; they are optional. + +### FS (Embedded Migrations) + +```go +//go:embed db/migration/*.sql +var migrations embed.FS + +config := goway.Configure(). + DataSource(db). + FS(migrations, "db/migration"). + Load() +``` + +Optional. Register an `fs.FS` (such as one created with `go:embed`) alongside the directory paths inside it that contain migrations. You can call `.FS()` multiple times to add more file systems. If you pass an empty path list, it defaults to `"."`. + +:::tip +Embedding migrations into your binary guarantees they are always available, even if the filesystem changes. This is recommended for production deployments. +::: + +## Schemas and History Table + +Manage which schemas goway touches and where it records its history. + +### Schemas + +```go +config := goway.Configure(). + DataSource(db). + Schemas("public", "audit", "internal"). + Load() +``` + +Optional. List the schemas managed by the migrator. The first schema also becomes the default schema. On PostgreSQL, the first schema is passed to `search_path` during migration execution. On SQLite, this setting has no effect (SQLite has no schemas). + +### DefaultSchema + +```go +config := goway.Configure(). + DataSource(db). + Schemas("public", "audit"). + DefaultSchema("audit"). + Load() +``` + +Optional. Override which schema holds the schema history table. By default, the first schema from `.Schemas()` is used. The history table is always created in this schema. + +### CreateSchemas + +```go +config := goway.Configure(). + DataSource(db). + CreateSchemas(false). + Load() +``` + +Default: `true` + +Controls whether goway automatically creates missing schemas during migration. Set to `false` if you want to manage schema creation yourself. + +### Table + +```go +config := goway.Configure(). + DataSource(db). + Table("migration_history"). + Load() +``` + +Default: `flyway_schema_history` + +The name of the schema history table. The table is created automatically in the default schema if it does not exist. The table structure matches Flyway's for drop-in compatibility. + +## Baseline + +Configure baseline behavior for existing databases. + +### BaselineVersion + +```go +config := goway.Configure(). + DataSource(db). + BaselineVersion("2024.1"). + Load() +``` + +Default: `"1"` + +The version recorded when you run the `baseline` command, and the lowest version below which migrations are ignored. Versions use dot or underscore separators (e.g., `"2024.1"`, `"1.0.0"`, `"3"`). + +### BaselineDescription + +```go +config := goway.Configure(). + DataSource(db). + BaselineDescription("Initial production schema"). + Load() +``` + +Default: `"<< Flyway Baseline >>"` + +The description recorded by the baseline command. + +### BaselineOnMigrate + +```go +config := goway.Configure(). + DataSource(db). + BaselineOnMigrate(true). + Load() +``` + +Default: `false` + +When enabled, if the database schema exists but the history table does not, goway automatically baseline the schema on the first migrate. This is useful for bringing existing databases under migration control without a separate baseline step. + +## Placeholders + +Substitute values into your migration scripts at runtime. + +### Placeholders + +```go +config := goway.Configure(). + DataSource(db). + Placeholders(map[string]string{ + "owner": "app_user", + "env": "production", + }). + Load() +``` + +Default: empty map + +Pass a map of key-value pairs to be substituted into scripts. Keys are matched case-insensitively. Placeholders do not affect checksums—only the raw file content is used for checksums, so changing a placeholder value and re-running a migration will not re-execute it. + +The built-in placeholders are always available: + +- `${flyway:defaultSchema}` — the default schema name +- `${flyway:user}` — the database user +- `${flyway:table}` — the history table name +- `${flyway:filename}` — the current migration script filename + +### PlaceholderPrefix and PlaceholderSuffix + +```go +config := goway.Configure(). + DataSource(db). + PlaceholderPrefix("[["). + PlaceholderSuffix("]]"). + Placeholders(map[string]string{ + "owner": "app_user", + }). + Load() +``` + +Defaults: `"${"` and `"}"` + +Customize the delimiters used to mark placeholders in scripts. Use this if your SQL already uses the default `${}` syntax for PostgreSQL dollar quoting or other constructs. + +## Migration Naming + +Override the prefixes, separator, and file suffixes that goway uses to identify and parse migration scripts. + +### SQLMigrationPrefix and RepeatableSQLMigrationPrefix + +```go +config := goway.Configure(). + DataSource(db). + SQLMigrationPrefix("M"). + RepeatableSQLMigrationPrefix("X"). + Load() +``` + +Defaults: `"V"` and `"R"` + +The prefixes that mark versioned and repeatable migrations respectively. By default, versioned migrations start with `V` (e.g., `V1__init.sql`, `V2__add_index.sql`) and repeatable ones start with `R` (e.g., `R__create_views.sql`). + +### SQLMigrationSeparator + +```go +config := goway.Configure(). + DataSource(db). + SQLMigrationSeparator("--"). + Load() +``` + +Default: `"__"` + +The token separating the version from the description in a versioned migration filename. Changing this to `--` allows you to use names like `V1--init.sql`. + +### SQLMigrationSuffixes + +```go +config := goway.Configure(). + DataSource(db). + SQLMigrationSuffixes(".sql", ".ddl"). + Load() +``` + +Default: `[".sql"]` + +The file suffixes recognized as migration scripts. By default, only `.sql` files are scanned. You can add others like `.ddl`, `.plsql`, etc., if your scripts use different extensions. + +## Safety Controls + +These settings protect you from unintended consequences. + +### ValidateOnMigrate + +```go +config := goway.Configure(). + DataSource(db). + ValidateOnMigrate(false). + Load() +``` + +Default: `true` + +When enabled, `migrate` runs validation before applying any migrations. Validation checks for missing, duplicate, and out-of-order migrations. If validation fails, no migrations are applied. Disable only if you have a specific reason to skip this check. + +### CleanDisabled + +```go +config := goway.Configure(). + DataSource(db). + CleanDisabled(false). + Load() +``` + +Default: `true` + +When enabled (the default), the `clean` command is disabled to prevent accidental deletion of all objects in your schema. Set to `false` to allow clean operations. This is recommended only in development. + +:::caution +The `clean` command drops all objects in every managed schema. Use it only when you fully understand the consequences. +::: + +### OutOfOrder + +```go +config := goway.Configure(). + DataSource(db). + OutOfOrder(true). + Load() +``` + +Default: `false` + +When enabled, allows migrations with a version lower than the current schema version to be applied. By default, this is not permitted to help catch migration ordering mistakes. Enable only when you deliberately want to insert migrations into the past. + +### Target + +```go +config := goway.Configure(). + DataSource(db). + Target("2024.1"). + Load() +``` + +Optional. Limit the highest version that `migrate` will apply. Useful for testing migrations without applying the latest versions, or for gradual deployments. + +## Other Settings + +### InstalledBy + +```go +config := goway.Configure(). + DataSource(db). + InstalledBy("deployment-bot"). + Load() +``` + +Optional. Override the user name recorded in the history table for applied migrations. By default, the database user is used. + +### Callbacks + +```go +config := goway.Configure(). + DataSource(db). + Callbacks(myCallback). + Load() +``` + +Optional. Register programmatic callbacks invoked during migration runs. In addition, goway discovers and executes SQL callback scripts from your locations: `beforeMigrate.sql`, `afterMigrate.sql`, `beforeEachMigrate.sql`, and `afterEachMigrate__.sql`. + +## Loading the Configuration + +Once you have configured all settings, call `.Load()` or `.LoadContext(ctx)` to finalize and validate the configuration: + +```go +db, _ := sql.Open("pgx", "postgresql://...") + +migrator, err := goway.Configure(). + DataSource(db). + Locations("db/migration"). + Load() +if err != nil { + log.Fatal(err) +} + +result, err := migrator.Migrate(context.Background()) +``` + +`.Load()` returns a `*Migrator` ready for use, or an error if the configuration is invalid (e.g., missing data source, invalid version format, unreachable migration locations). + +For more detail on every configuration option, see the [full API reference](/goway/reference/configuration/). diff --git a/docs/src/content/docs/guides/dialects.md b/docs/src/content/docs/guides/dialects.md new file mode 100644 index 0000000..c38bbdc --- /dev/null +++ b/docs/src/content/docs/guides/dialects.md @@ -0,0 +1,135 @@ +--- +title: Dialects +description: How goway supports PostgreSQL and SQLite, and what differs between them. +--- + +goway supports two databases: PostgreSQL and SQLite. Each has its own dialect implementation that handles differences in SQL syntax, schema handling, and features. + +## Dialect auto-detection + +When you configure goway with a live database connection, the dialect is detected automatically by attempting to query a database-specific function. SQLite's `sqlite_version()` and PostgreSQL's `version()` functions serve as discriminators: + +```go +migrator, err := goway.Configure(). + DataSource(database). + Load() // dialect auto-detected from the connection +if err != nil { + return err +} +``` + +If auto-detection fails, an error is returned. You can specify a dialect explicitly using the `.Dialect()` setter: + +```go +migrator, err := goway.Configure(). + DataSource(database). + Dialect(goway.Postgres()). + Load() +if err != nil { + return err +} +``` + +## Supported versions + +goway targets the two most recent major versions of each database: + +- **PostgreSQL**: versions 16 and 17 +- **SQLite**: versions 3.43 and 3.44 + +Older versions may work but are not tested or supported. + +## Drivers + +goway does not bundle any database drivers. Your application must import and register the appropriate driver for the database you use. + +### PostgreSQL with pgx + +PostgreSQL connections use the [`jackc/pgx/v5`](https://github.com/jackc/pgx) driver: + +```go +import _ "github.com/jackc/pgx/v5/stdlib" + +// ... + +db, err := sql.Open("pgx", "postgresql://user:password@localhost/dbname") +if err != nil { + return err +} +``` + +### SQLite with modernc.org/sqlite + +SQLite uses the pure Go [`modernc.org/sqlite`](https://pkg.go.dev/modernc.org/sqlite) driver, which requires no C dependencies: + +```go +import _ "modernc.org/sqlite" + +// ... + +db, err := sql.Open("sqlite", "path/to/database.db") +if err != nil { + return err +} +``` + +## Feature comparison + +The two dialects differ in several capabilities: + +| Feature | PostgreSQL | SQLite | +|---------|-----------|--------| +| Addressable schemas | Yes | No | +| Per-transaction search path | Yes | No | +| Transactional DDL | Yes | Yes | +| Dollar-quoted string bodies | Yes | No | +| Trigger block splitting | No | Yes | +| Advisory migration lock | Yes | No | + +### Schemas + +PostgreSQL supports addressable schemas, allowing you to run migrations against multiple named schemas in the same database. SQLite has no concept of schemas and addresses all tables within a single attached database. + +When using PostgreSQL, you can specify target schemas with the `.Schemas()` configuration method. goway creates these schemas if they do not exist (unless disabled with `.CreateSchemas(false)`) and runs all migrations against them. + +### Search path + +On PostgreSQL, the default schema for unqualified table and object references can be controlled per transaction or per session. Each migration transaction sets the search path to include the target schema, so references like `CREATE TABLE users` (without a schema qualifier) apply to the correct schema. + +SQLite has no equivalent concept, so all table references must be unqualified (the database has only one implicit schema). + +### Identifier quoting + +Both dialects quote identifiers using double quotes: `"table_name"`, `"column_name"`. This is the standard SQL behavior for both PostgreSQL and SQLite. + +### Statement splitting + +Migrations are split into individual executable statements. Both dialects understand standard SQL statement terminators and block syntax: + +- **PostgreSQL** understands parentheses, `BEGIN`/`END`, `CASE`/`END`, and quoted strings. +- **SQLite** also understands trigger blocks (`BEGIN ... END` inside `CREATE TRIGGER`), backtick identifiers, and `[bracket]` identifiers. + +This ensures that complex statements like triggers and stored procedures are correctly parsed as single units, even if they contain semicolons. + +### Non-transactional migrations + +Some operations cannot run inside a transaction. Prefix your migration with a comment to exempt it: + +```sql +-- goway:noTransaction +CREATE INDEX CONCURRENTLY idx_name ON table_name (column); +``` + +(The Flyway-compatible form `-- flyway:executeInTransaction=false` is also recognized.) + +On PostgreSQL, this allows statements like `CREATE INDEX CONCURRENTLY` that require a transaction-free environment. On SQLite, certain operations like `VACUUM` and `PRAGMA` statements require the same treatment. + +Non-transactional migrations run on a dedicated connection outside the per-migration transaction. If a non-transactional migration fails, a failed history row is recorded; use the [Repair](/goway/reference/commands/) command to clean it up. + +## Single-writer concurrency on SQLite + +SQLite enforces single-writer semantics at the database level. Only one connection may write at a time; other connections are blocked until the write completes. This means that concurrent migration attempts will serialize and only the first will make progress. For applications with a single initialization point (typical in Go applications that run migrations on startup), this is not a practical concern. + +## Next steps + +See the [Configuration reference](/goway/reference/configuration/) for all available options, and [Writing Migrations](/goway/guides/writing-migrations/) for migration naming and syntax. diff --git a/docs/src/content/docs/guides/running-migrations.md b/docs/src/content/docs/guides/running-migrations.md new file mode 100644 index 0000000..11833ca --- /dev/null +++ b/docs/src/content/docs/guides/running-migrations.md @@ -0,0 +1,241 @@ +--- +title: Running Migrations +description: Build a migrator with the fluent configuration and run migrate, info, and validate. +--- + +## Overview + +The typical workflow is to configure migrations at application startup, then invoke the migrate command. The configuration is fluent—each setter returns the `Configuration` so calls can be chained—and finalized by calling `Load()`, which returns a `Migrator` ready to run commands. + +## Configuration and Loading + +All goway operations begin with `Configure()`, a function that returns a `*Configuration` with Flyway-compatible defaults. From there, you chain setters for your data source, dialect, migration locations, and any other options: + +```go +package main + +import ( + "context" + "database/sql" + _ "github.com/jackc/pgx/v5/stdlib" + "github.com/cgardev/goway" +) + +func main() { + db, err := sql.Open("pgx", "postgres://localhost/mydb") + if err != nil { + panic(err) + } + defer db.Close() + + migrator, err := goway.Configure(). + DataSource(db). + Locations("db/migration"). + Load(context.Background()) + if err != nil { + panic(err) + } + + result, err := migrator.Migrate(context.Background()) + if err != nil { + panic(err) + } + println("Applied", result.MigrationsExecuted, "migrations") +} +``` + +### Data Source + +Every configuration must include a database connection set with `DataSource()`. The connection pool is used for all operations and must remain open for the lifetime of commands: + +```go +db, err := sql.Open("pgx", "postgres://localhost/mydb") +// or +db, err := sql.Open("sqlite", "file.db") +``` + +### Dialect Detection + +By default, the dialect is detected automatically from the database when you call `Load()` or `LoadContext()`. If detection fails or you need to override it, set the dialect explicitly: + +```go +migrator, err := goway.Configure(). + DataSource(db). + Dialect(goway.Postgres()). + Load(context.Background()) +``` + +Currently supported dialects are `Postgres()` and `SQLite()`. + +### Migration Locations + +Migrations are discovered from one or more locations. The default is `"db/migration"` relative to the working directory. Locations can use `"filesystem:"` or `"classpath:"` prefixes (or no prefix), and `Load()` tolerates missing directories: + +```go +goway.Configure(). + Locations("filesystem:db/migration", "db/migrations/custom"). + Load(ctx) +``` + +### Embedding Migrations with go:embed + +For a production binary, embed migrations directly using Go's `embed` package and register the embedded file system with `FS()`: + +```go +import "embed" + +//go:embed migrations/*.sql +var migrations embed.FS + +migrator, err := goway.Configure(). + DataSource(db). + FS(migrations, "migrations"). + Load(context.Background()) +``` + +You can register multiple embedded file systems, and they are scanned in the order added, before any filesystem locations. + +## Running Migrate + +The `Migrate()` command brings the database schema up to date by applying every pending migration in ascending version order. It is safe to call multiple times and idempotent—migrations that have already been applied are skipped. + +### Step-by-Step + +When you call `Migrate()`, the migrator performs these steps: + +1. **Acquires an exclusive lock** to ensure only one migrator runs at a time. On PostgreSQL this is a session advisory lock; SQLite is single-writer by nature. +2. **Creates missing schemas** if configured with `Schemas()` and `CreateSchemas(true)` (the default). +3. **Creates the schema history table** if it does not exist. This table records every migration applied and is created in the default schema. +4. **Validates pending migrations** if `ValidateOnMigrate(true)` (the default). Validation checks for issues like missing scripts or checksum mismatches. +5. **Fires beforeMigrate callbacks**, allowing custom logic before any migration runs. +6. **Applies each pending migration in a transaction** (unless the script opts out with `-- goway:noTransaction`). Statements are executed in order, and the history row is recorded in the same transaction, so a failure rolls back both and leaves no partial state. +7. **Fires afterMigrate callbacks** after all migrations succeed. + +If any step fails, the error is returned and no further migrations are applied. A failed migration can be retried after the script is corrected. + +### MigrateResult + +The result includes the initial and target schema versions, the number of migrations executed, and detailed information about each migration applied: + +```go +result, err := migrator.Migrate(ctx) +if err != nil { + panic(err) +} + +println("Initial version:", result.InitialSchemaVersion) +println("Target version:", result.TargetSchemaVersion) +println("Applied", result.MigrationsExecuted, "migrations") + +for _, migration := range result.Migrations { + println("Applied", migration.Version, "-", migration.Description) +} +``` + +:::note +If validation fails during migrate and `ValidateOnMigrate(true)`, the error wraps `goway.ErrValidationFailed`. Use `errors.Is(err, goway.ErrValidationFailed)` to distinguish validation errors from other failures. +::: + +### At Application Startup + +A common pattern is to run migrations automatically on application startup, before serving requests: + +```go +func main() { + db, err := sql.Open("pgx", os.Getenv("DATABASE_URL")) + if err != nil { + log.Fatal(err) + } + defer db.Close() + + migrator, err := goway.Configure(). + DataSource(db). + Locations("db/migration"). + Load(context.Background()) + if err != nil { + log.Fatal("load migrator:", err) + } + + result, err := migrator.Migrate(context.Background()) + if err != nil { + log.Fatal("migrate:", err) + } + log.Printf("applied %d migrations (v%s → v%s)", + result.MigrationsExecuted, + result.InitialSchemaVersion, + result.TargetSchemaVersion) + + // now safe to use the database + runServer(db) +} +``` + +## Inspecting State with Info + +The `Info()` command reports the state of every resolved migration (both applied and pending) without changing the database: + +```go +result, err := migrator.Info(ctx) +if err != nil { + panic(err) +} + +println("Current schema version:", result.Current) +for _, migration := range result.Migrations { + println(migration.Version, "-", migration.Description, "-", migration.State) +} +``` + +The `State` field is one of: +- `Success` – applied and checksum matches +- `Pending` – not yet applied +- `Failed` – applied but execution failed (can be repaired) +- `Out of Order` – applied but has a version lower than the current one +- `Missing` – was applied but the script no longer exists +- `Outdated` – repeatable migration that is out of date (checksum mismatch) + +This is useful for diagnostics and logging. + +## Validating Migrations with Validate + +The `Validate()` command checks that applied migrations match the resolved scripts. It detects problems such as: +- A checksum mismatch (script modified after application) +- A failed migration +- A missing migration (was applied but script is gone) +- A pending migration (resolved but not yet applied) + +```go +result, err := migrator.Validate(ctx) +if err != nil { + panic(err) +} + +if !result.Valid { + for _, e := range result.Errors { + println("Error:", e.Message) + } + panic("validation failed") +} + +println("All", result.ValidationCount, "migrations are valid") +``` + +Validation does not require any database changes and can be run repeatedly. When validation fails, the returned error wraps `goway.ErrValidationFailed`; you can still read the result to inspect the specific problems. + +:::caution +Do not modify migration scripts after they have been applied. Doing so will cause checksum validation to fail. If you need to make a change after application, create a new migration (preferably a repeatable one for idempotent changes). +::: + +## Configuration Reference + +For a complete list of configuration options and their defaults, see the [Configuration Reference](/goway/reference/configuration/). + +For detailed command signatures and result types, see the [Commands Reference](/goway/reference/commands/). + +## Advanced Topics + +- **Embedding migrations**: See [Embedding Migrations](/goway/cookbook/embedding-migrations/) for patterns using `go:embed` and `FS()`. +- **Writing migrations**: See [Writing Migrations](/goway/guides/writing-migrations/) for migration naming, syntax, and repeatable patterns. +- **Non-transactional scripts**: Use `-- goway:noTransaction` to run statements like PostgreSQL's `CREATE INDEX CONCURRENTLY` outside a transaction. +- **Placeholders**: Customize placeholder delimiters with `PlaceholderPrefix()` and `PlaceholderSuffix()`, and supply values with `Placeholders()`. +- **Callbacks**: Use `Callbacks()` to register programmatic hooks (beforeMigrate, afterMigrate, etc.), or place SQL scripts in locations named `beforeMigrate.sql`, `afterMigrate.sql`, etc. diff --git a/docs/src/content/docs/guides/writing-migrations.md b/docs/src/content/docs/guides/writing-migrations.md new file mode 100644 index 0000000..449011a --- /dev/null +++ b/docs/src/content/docs/guides/writing-migrations.md @@ -0,0 +1,195 @@ +--- +title: Writing Migrations +description: The naming convention, versioned vs repeatable migrations, multi-statement scripts, and checksums. +--- + +## File Naming Convention + +Migration files follow a strict naming convention to be recognized and ordered correctly. The format is: + +``` + +``` + +**Versioned migrations** (run once): +``` +V__.sql +``` + +**Repeatable migrations** (re-run when checksum changes): +``` +R__.sql +``` + +### Default Values + +| Component | Default | Configurable | +|-----------|---------|--------------| +| Versioned prefix | `V` | `SQLMigrationPrefix()` | +| Repeatable prefix | `R` | `RepeatableSQLMigrationPrefix()` | +| Separator | `__` (double underscore) | `SQLMigrationSeparator()` | +| Suffix | `.sql` | `SQLMigrationSuffixes()` | + +### Naming Examples + +| Filename | Type | Version | Description | +|----------|------|---------|-------------| +| `V1__init.sql` | Versioned | `1` | init | +| `V1.1__add_users_table.sql` | Versioned | `1.1` | add users table | +| `V2_0_1__create_index.sql` | Versioned | `2.0.1` | create index | +| `R__refresh_materialized_view.sql` | Repeatable | (none) | refresh materialized view | +| `R__latest_countries.sql` | Repeatable | (none) | latest countries | + +Underscores in the description are converted to spaces in the migration history table. Underscores in the version are converted to dots for parsing purposes, allowing file systems that dislike dots to use underscores instead (e.g., `V2_0_1` parses as version `2.0.1`). + +## Version Numbering + +Versions are sequences of non-negative integer components separated by dots or underscores. Examples: + +- `1` — a single component +- `1.1` — two components +- `2.0.1` — three components +- `20260531` — a date-based version (arbitrary precision) + +### Version Comparison and Equality + +Versions are compared component-by-component, with missing trailing components treated as zero. This means: + +- `1` equals `1.0` equals `1.0.0` +- `2` is less than `2.1` (the second component defaults to zero in the first version) +- `10` is greater than `2` (numeric comparison, not string-based) + +Trailing zeros are automatically removed from the internal representation, so duplicate versions using different forms (e.g., `1` and `1.0`) are correctly detected and rejected. + +## Versioned vs Repeatable Migrations + +### Versioned Migrations + +Versioned migrations run **once**, in ascending version order. Use them for: + +- Creating tables +- Adding columns or indexes +- One-off data transformations +- Any schema change that should be applied exactly once + +Once a versioned migration is executed, it is never run again, even if you modify the file. + +### Repeatable Migrations + +Repeatable migrations run **every time** their checksum changes, after all versioned migrations. Use them for: + +- Views and materialized views +- Functions and stored procedures +- Refresh operations +- Idempotent utility scripts + +Repeatable migrations are sorted by description (alphabetically) and are applied in that order. If you change the content of a repeatable migration, the system will detect the checksum change and re-apply it on the next migration run. + +:::note +Both versioned and repeatable migrations must follow the naming convention and file suffix rules. Files that do not match are ignored (unless they are callback scripts). +::: + +## Multi-Statement Scripts + +A migration script can contain multiple SQL statements separated by semicolons. The parser understands SQL syntax and will not incorrectly split on semicolons that appear inside strings, comments, or other constructs. + +### Syntax Awareness + +The parser respects: + +- **Single-quoted strings** — e.g., `'O''Brien'` (doubled quotes are escaped) +- **Double-quoted identifiers** — e.g., `"Column Name"` +- **Line comments** — `-- comment text` (to end of line) +- **Block comments** — `/* nested /* comments */ supported */` +- **PostgreSQL dollar-quoted strings** — e.g., `$$function body$$` or `$fn$body$fn$` + - Dollar quotes can contain an optional identifier: `$function$...$function$` + - The identifier must start with a letter or underscore +- **PostgreSQL escape strings** — e.g., `E'line\nbreak'` +- **SQLite backtick identifiers** — e.g., `` `Column` `` +- **SQLite bracket identifiers** — e.g., `[Column Name]` +- **SQLite trigger blocks** — `BEGIN...END` blocks in CREATE TRIGGER statements are treated as nested, so their inner semicolons do not split statements +- **SQLite CASE expressions** — `CASE...END` blocks are nested (do not split on their inner semicolons) + +### Statement Extraction + +After splitting, empty statements (containing only whitespace) are discarded. Each non-empty statement is trimmed and executed separately. Comments and whitespace within statements are preserved. + +### Example Multi-Statement Script + +```sql +-- Initialize the schema +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL +); + +-- Add an index +CREATE INDEX idx_users_name ON users(name); + +-- Insert initial data +INSERT INTO users (name) VALUES + ('Alice'), + ('Bob'), + ('Charlie'); +``` + +This script contains three statements: CREATE TABLE, CREATE INDEX, and INSERT. + +## Non-Transactional Migrations + +By default, each SQL migration runs inside its own database transaction, which is committed if the migration succeeds or rolled back if it fails. Some operations cannot run inside a transaction—for example, PostgreSQL's `CREATE INDEX CONCURRENTLY` or SQLite's `VACUUM`. + +To run a migration outside a transaction, add a comment directive at the start of the file: + +```sql +-- goway:noTransaction +CREATE INDEX CONCURRENTLY idx_large_table ON large_table(column); +``` + +The alternative Flyway-compatible form is also accepted: + +```sql +-- flyway:executeInTransaction=false +CREATE INDEX CONCURRENTLY idx_large_table ON large_table(column); +``` + +Non-transactional migrations are executed on a dedicated database connection. If a non-transactional migration fails, a history row is recorded with `success=false`. Use the [Repair](/goway/reference/commands/) command to clear failed migration records before retrying. + +:::caution +Non-transactional migrations should be idempotent (safe to retry). Include `IF NOT EXISTS` or similar guards. +::: + +For more details and examples, see the [Cookbook](/goway/cookbook/). + +## Checksums + +Each SQL migration file is assigned a checksum to detect unintended changes. The checksum is computed using the **CRC32 algorithm (IEEE)** over the raw file content. + +### How Checksums Are Computed + +1. The file content is split into lines (respecting CR, LF, and CRLF line terminators) +2. The UTF-8 byte order mark (BOM), if present on the first line, is stripped +3. The raw bytes of each line (excluding line terminators) are hashed using CRC32 +4. The result is interpreted as a signed 32-bit integer + +### Checksum Storage and Validation + +When a migration is executed, its checksum is stored in the schema history table. On subsequent runs: + +- For **versioned migrations**: the stored checksum is ignored; the migration is not re-executed +- For **repeatable migrations**: the current checksum is compared to the stored one; if they differ, the migration is re-executed and the checksum is updated +- During `validate`: checksums are verified; a mismatch indicates the file has been modified since execution + +### Changing a Migration File + +**Versioned migration:** Do not modify the content of a migration after it has been applied to production. If you need to make a change, create a new versioned migration (with a higher version number) to apply the fix. + +**Repeatable migration:** Modifying the file will cause it to be re-executed on the next migration run. Ensure the changes are idempotent—safe to run multiple times. + +:::tip +If you accidentally modify a versioned migration and need to recover, use the [Repair](/goway/reference/commands/) command to fix the checksum mismatch. However, the best practice is to create a new migration instead. +::: + +--- + +**Next:** See [Configuration](/goway/guides/configuration/) to set up your migration source locations and database connection. diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx new file mode 100644 index 0000000..7811887 --- /dev/null +++ b/docs/src/content/docs/index.mdx @@ -0,0 +1,84 @@ +--- +title: goway +description: A version-based database schema migration library for Go, modeled on Flyway, for PostgreSQL and SQLite. +template: splash +hero: + tagline: A version-based database schema migration library for Go, modeled on Flyway. Discover SQL migrations, record them in a schema history table, and bring PostgreSQL or SQLite up to date — with a zero-dependency core. + image: + file: ../../assets/sql-file.png + actions: + - text: Get Started + link: /goway/getting-started/ + icon: right-arrow + - text: View on GitHub + link: https://github.com/cgardev/goway + icon: external + variant: minimal +--- + +import { Card, CardGrid } from '@astrojs/starlight/components'; + +## Why goway? + +goway brings Flyway's proven migration model to Go: immutable, ordered, +checksummed SQL migrations recorded in a schema history table. It runs inside +your own application using the connection pool you already own, so there is no +external binary to install and no heavyweight framework to adopt. + + + + The library depends only on the standard library. Database access goes + through `database/sql`, so you bring your own driver. + + + First-class support for both, with SQLite handled by the pure-Go + `modernc.org/sqlite` driver — no cgo required. + + + The same `V`/`R` naming, the same CRC32 checksums, and the same + `flyway_schema_history` table, so existing migration sets just work. + + + Read migrations from the file system or embed them into your binary with + `go:embed`. Run migrate at startup, in tests, or from the CLI. + + + +## Installation + +```sh +go get github.com/cgardev/goway +``` + +## A Quick Taste + +```go +migrator, err := goway.Configure(). + DataSource(database). + Locations("filesystem:db/migration"). + Schemas("public"). + Load() +if err != nil { + log.Fatal(err) +} + +result, err := migrator.Migrate(context.Background()) +// applied N migration(s); schema is now at version X +``` + +[Get Started →](/goway/getting-started/) + +## What's Next? + + + + Learn the core concepts step-by-step, from writing your first migration to + callbacks and dialects. + + + Detailed documentation for every part of the API, from the configuration + builder to the schema history table. + + + +[Browse the Guides →](/goway/guides/writing-migrations/) diff --git a/docs/src/content/docs/reference.md b/docs/src/content/docs/reference.md new file mode 100644 index 0000000..3bf1082 --- /dev/null +++ b/docs/src/content/docs/reference.md @@ -0,0 +1,22 @@ +--- +title: Reference Overview +description: Index of the goway API reference. +--- + +This reference section documents the goway API, command line interface, and key concepts. Start with the page that matches your task: + +- **[Configuration](/goway/reference/configuration/)** — Configure the migrator with a fluent builder, specifying the database connection, migration locations, schemas, and behavior options. + +- **[Commands](/goway/reference/commands/)** — Execute migration commands on your database: Migrate, Info, Validate, Baseline, Repair, and Clean. + +- **[Migration Naming](/goway/reference/migration-naming/)** — Understand the versioned and repeatable migration file naming conventions and version comparison rules. + +- **[Schema History](/goway/reference/schema-history/)** — Learn how goway tracks applied migrations in the schema history table. + +- **[Placeholders](/goway/reference/placeholders/)** — Use runtime placeholders in your migration scripts to inject configuration values at execution time. + +- **[Callbacks](/goway/reference/callbacks/)** — Hook into the migration lifecycle with callbacks that run before and after migrations. + +- **[Dialects](/goway/reference/dialects/)** — Understand the PostgreSQL and SQLite database dialects and their differences. + +- **[Command Line](/goway/reference/cli/)** — Use the goway CLI to run migrations from the terminal with flags and environment variables. diff --git a/docs/src/content/docs/reference/callbacks.md b/docs/src/content/docs/reference/callbacks.md new file mode 100644 index 0000000..aaca94b --- /dev/null +++ b/docs/src/content/docs/reference/callbacks.md @@ -0,0 +1,238 @@ +--- +title: Callbacks +description: The callback events, the Callback interface, CallbackFunc, and SQL callback scripts. +--- + +Callbacks let you run custom code at specific points during a migrate operation. You can register programmatic callbacks in Go or discover SQL callback scripts in your migration locations. + +## Callback Events + +Four events fire during a migrate run. Each is identified by a `CallbackEvent` string constant: + +| Event | Constant | String Value | Timing | +|-------|----------|--------------|--------| +| Run-start | `EventBeforeMigrate` | `"beforeMigrate"` | Once, before any migration is applied | +| Run-end | `EventAfterMigrate` | `"afterMigrate"` | Once, after all migrations have been applied | +| Per-migration start | `EventBeforeEachMigrate` | `"beforeEachMigrate"` | Before each individual migration | +| Per-migration end | `EventAfterEachMigrate` | `"afterEachMigrate"` | After each individual migration | + +The `beforeMigrate` and `afterMigrate` callbacks fire at the run level; `beforeEachMigrate` and `afterEachMigrate` fire within each migration's scope. This distinction affects what database connection and transaction context they receive. + +## Event Order + +During a `Migrate()` call, events fire in this order: + +1. **beforeMigrate** (run-level, receives *sql.DB) +2. For each pending migration: + - **beforeEachMigrate** (migration-level, on the migration's executor) + - Migration statements execute + - **afterEachMigrate** (migration-level, on the migration's executor) +3. **afterMigrate** (run-level, receives *sql.DB) + +If any callback returns an error, the migrate stops and the error is returned. For migrations within a transaction, the transaction rolls back; for non-transactional migrations, a failed history row is recorded. + +## The Callback Interface + +Implement the `Callback` interface to register a programmatic callback: + +```go +type Callback interface { + Handle(ctx context.Context, event CallbackEvent, exec Execer, migration *MigrationInfo) error +} +``` + +### Handle Signature + +- **ctx** (`context.Context`): The context passed to the migrator command (e.g., `Migrate(ctx)`). +- **event** (`CallbackEvent`): The event being fired—one of the four constants above. +- **exec** (`Execer`): The database executor. For `beforeEachMigrate` and `afterEachMigrate`, this is the migration's transaction (or connection for non-transactional migrations). For `beforeMigrate` and `afterMigrate`, this is the `*sql.DB` pool. +- **migration** (`*MigrationInfo`): For per-migration events, a pointer to the `MigrationInfo` of the migration being applied. For run-level events, this is `nil`. + +### Execer Interface + +The `Execer` interface defines what operations you can perform: + +```go +type Execer interface { + ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) +} +``` + +It is satisfied by `*sql.DB`, `*sql.Tx`, and `*sql.Conn`. A callback receives the appropriate executor for its event: + +- Run-level callbacks (`beforeMigrate`, `afterMigrate`) receive the `*sql.DB` pool. +- Per-migration callbacks (`beforeEachMigrate`, `afterEachMigrate`) receive the executor of the migration's transaction (or dedicated connection if the migration opted out of transactions). + +## CallbackFunc + +For simple cases, adapt an ordinary function to the `Callback` interface using `CallbackFunc`: + +```go +type CallbackFunc func(ctx context.Context, event CallbackEvent, exec Execer, migration *MigrationInfo) error + +func (f CallbackFunc) Handle(ctx context.Context, event CallbackEvent, exec Execer, migration *MigrationInfo) error { + return f(ctx, event, exec, migration) +} +``` + +This allows you to pass a function directly to `Configure().Callbacks(...)` without defining a type. + +## SQL Callback Scripts + +In addition to programmatic callbacks, goway discovers SQL callback scripts in your configured migration locations. These are named to match a callback event and a naming pattern. + +### Naming Convention + +SQL callback scripts are named after the event, optionally followed by the separator and a description: + +- **beforeMigrate.sql** – Runs once before any migration. +- **afterMigrate.sql** – Runs once after all migrations. +- **beforeEachMigrate.sql** – Runs before each migration. +- **beforeEachMigrate__.sql** – Per-migration variant with a description (separator configurable). +- **afterEachMigrate.sql** – Runs after each migration. +- **afterEachMigrate__.sql** – Per-migration variant with a description. + +The separator is configurable via `Configure().SQLMigrationSeparator(...)` and defaults to `__`. + +### Example Script Names + +``` +beforeMigrate.sql +afterMigrate.sql +beforeEachMigrate.sql +beforeEachMigrate__log_start.sql +afterEachMigrate__log_end.sql +afterEachMigrate__vacuum.sql +``` + +All matching scripts are discovered and executed in alphabetical order for their event. + +### Execution Context + +Like programmatic callbacks, SQL scripts run on their event's executor: + +- Scripts for `beforeMigrate` and `afterMigrate` run on the `*sql.DB` pool. +- Scripts for `beforeEachMigrate` and `afterEachMigrate` run on the migration's transaction (or connection). + +### Placeholder Support + +SQL callback scripts support the same [placeholder substitution](/goway/reference/placeholders/) as migration scripts. Built-in placeholders like `${flyway:user}` and `${flyway:defaultSchema}` are always available. + +## Registering Callbacks + +Pass `Callback` implementations to `Configure().Callbacks(...)`: + +```go +m, err := goway.Configure(). + DataSource(db). + Callbacks( + myCallback1, + myCallback2, + goway.CallbackFunc(func(ctx context.Context, event goway.CallbackEvent, exec goway.Execer, migration *goway.MigrationInfo) error { + // inline callback logic + return nil + }), + ). + Load() +if err != nil { + return err +} +``` + +Callbacks are registered in order and invoked in order for their event. SQL callback scripts are run before programmatic callbacks for the same event. + +## Complete Example + +Here is a complete example that registers both SQL callback scripts and a programmatic callback: + +```go +package main + +import ( + "context" + "database/sql" + "fmt" + "log" + + _ "github.com/jackc/pgx/v5/stdlib" + "github.com/cgardev/goway" +) + +type MigrationLogger struct { + logTable string +} + +func (l *MigrationLogger) Handle(ctx context.Context, event goway.CallbackEvent, exec goway.Execer, migration *goway.MigrationInfo) error { + switch event { + case goway.EventBeforeMigrate: + return l.logEvent(ctx, exec, "BEFORE_MIGRATE", migration) + case goway.EventAfterMigrate: + return l.logEvent(ctx, exec, "AFTER_MIGRATE", migration) + case goway.EventBeforeEachMigrate: + return l.logEvent(ctx, exec, "BEFORE_EACH", migration) + case goway.EventAfterEachMigrate: + return l.logEvent(ctx, exec, "AFTER_EACH", migration) + } + return nil +} + +func (l *MigrationLogger) logEvent(ctx context.Context, exec goway.Execer, eventName string, migration *goway.MigrationInfo) error { + version := "N/A" + if migration != nil { + version = migration.Version + } + query := fmt.Sprintf( + "INSERT INTO %s (event, version, timestamp) VALUES ($1, $2, NOW())", + l.logTable, + ) + _, err := exec.ExecContext(ctx, query, eventName, version) + return err +} + +func main() { + db, err := sql.Open("pgx", "postgres://user:pass@localhost/mydb") + if err != nil { + log.Fatal(err) + } + defer db.Close() + + // Create the callback logger + logger := &MigrationLogger{logTable: "migration_log"} + + // Configure and load the migrator with both SQL scripts and a programmatic callback. + // Assuming your db/migration directory contains: + // - beforeMigrate.sql + // - afterEachMigrate__log_completion.sql + m, err := goway.Configure(). + DataSource(db). + Locations("db/migration"). + Callbacks(logger). + Load() + if err != nil { + log.Fatal(err) + } + + // Run migrations; all callbacks fire in order + result, err := m.Migrate(context.Background()) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Applied %d migrations\n", result.MigrationsExecuted) +} +``` + +In this example: + +1. The `beforeMigrate.sql` script (if present) runs once at the start. +2. For each migration, `beforeEachMigrate__*.sql` scripts run, then the migration statements, then `afterEachMigrate__*.sql` scripts. +3. The `MigrationLogger` callback also fires, logging each event to a table. +4. The `afterMigrate.sql` script (if present) runs once at the end. + +:::tip +Use per-migration callbacks to validate state, log progress, or clean up temporary objects. Use run-level callbacks to set up and tear down resources that span multiple migrations. +::: + +:::note +SQL callback scripts support the same placeholders as migrations and can reference objects in your configured schemas. The `${flyway:filename}` placeholder resolves to the script file name. +::: diff --git a/docs/src/content/docs/reference/cli.md b/docs/src/content/docs/reference/cli.md new file mode 100644 index 0000000..aaa7695 --- /dev/null +++ b/docs/src/content/docs/reference/cli.md @@ -0,0 +1,277 @@ +--- +title: Command Line Interface +description: "Complete reference for the cmd/goway command line tool: commands, flags, and URLs." +--- + +The `goway` CLI is a command-line front end for the goway migration library. It connects to a PostgreSQL or SQLite database and runs one of the six migration commands. + +## Installation + +Build and run the CLI directly: + +```sh +go run github.com/cgardev/goway/cmd/goway [flags] +``` + +Or install it locally: + +```sh +go install github.com/cgardev/goway/cmd/goway@latest +goway [flags] +``` + +## Database URL + +The database URL is required and must be provided either via the `-url` flag or the `GOWAY_URL` environment variable. + +### Supported Schemes + +**PostgreSQL:** +- `postgres://[user[:password]@][host][:port][/dbname][?param=value...]` +- `postgresql://[user[:password]@][host][:port][/dbname][?param=value...]` + +Connects via [pgx](https://github.com/jackc/pgx) and requires the stdlib driver: + +```go +import _ "github.com/jackc/pgx/v5/stdlib" +``` + +**SQLite:** +- `sqlite:./path/to/db.sqlite` or `sqlite:db.sqlite` +- `file:/path/to/db.sqlite` (absolute path) or `file:///path/to/db.sqlite` + +Connects via [modernc.org/sqlite](https://pkg.go.dev/modernc.org/sqlite) (pure Go): + +```go +import _ "modernc.org/sqlite" +``` + +## Flags + +| Flag | Type | Default | Description | +|------|------|---------|-------------| +| `-url` | string | `GOWAY_URL` env | Database URL (required). For example: `postgres://localhost/mydb` or `sqlite:./app.db`. | +| `-locations` | string | `filesystem:db/migration` | Comma-separated list of migration locations. Supports `filesystem:` URLs only in the CLI. | +| `-schemas` | string | (empty) | Comma-separated list of schemas. The first schema is treated as the default schema. | +| `-table` | string | `flyway_schema_history` | Name of the schema history table. | +| `-create-schemas` | bool | `true` | Automatically create missing schemas. | +| `-baseline-version` | string | `1` | Version to record when baselining. | +| `-baseline-description` | string | `<< Flyway Baseline >>` | Description to record when baselining. | +| `-baseline-on-migrate` | bool | `false` | Automatically baseline on the first migrate if no history exists. | +| `-out-of-order` | bool | `false` | Allow migrations to be applied out of order. | +| `-clean-disabled` | bool | `true` | Disable the `clean` command. Set to `false` to enable. | +| `-placeholders` | string | (empty) | Comma-separated key=value pairs for placeholder substitution (e.g., `env=prod,region=us-east-1`). | +| `-target` | string | (empty) | Highest version to apply. Stops migration at this version. | + +## Commands + +Exactly one command is required. + +### migrate + +Apply all pending versioned migrations in order, then repeatable migrations if any have changed checksums. + +```sh +goway -url postgres://localhost/mydb migrate +``` + +Output on success (when migrations were applied): + +``` +Successfully applied 3 migration(s) to version 2.1: + 1 initial schema + 2 add users table + 2.1 add index on user_id +``` + +Output when already up to date: + +``` +Schema is up to date. No migration necessary. (version 2.1) +``` + +### info + +Display the migration history and current schema version in a table. + +```sh +goway -url postgres://localhost/mydb info +``` + +Output: + +``` +Version Description Type State Installed On +1 initial schema SQL Success 2026-05-31 10:15:42 +2 add users SQL Success 2026-05-31 10:16:05 +2.1 add index SQL Success 2026-05-31 10:16:15 +R seed data SQL Success 2026-05-31 10:16:20 + +Current version: 2.1 +``` + +### validate + +Verify that all recorded migrations match their checksum and are in valid order. + +```sh +goway -url postgres://localhost/mydb validate +``` + +Output on success: + +``` +Validation successful: 4 migration(s) validated. +``` + +Output on failure (written to stderr): + +``` +Validation failed: + 1: Checksum mismatch for V1__initial_schema.sql + 2: Migration V1.5__alter_table.sql not found +``` + +### baseline + +Record the current schema version without applying any migrations. Useful when adopting goway on an existing database. + +```sh +goway -url postgres://localhost/mydb baseline +``` + +Output: + +``` +Successfully baselined schema at version 1. +``` + +If already baselined: + +``` +Schema is already baselined at version 1. +``` + +### repair + +Remove failed migration entries from the history and update checksums of successful migrations to match their current content. + +```sh +goway -url postgres://localhost/mydb repair +``` + +Output: + +``` +Repair complete: removed 1 failed entry(ies), realigned 2 checksum(s). +``` + +### clean + +Drop all database objects in all configured schemas. **This command is disabled by default** for safety. Enable with `-clean-disabled=false`. + +```sh +goway -url postgres://localhost/mydb -clean-disabled=false clean +``` + +Output: + +``` +Successfully cleaned schema(s): public, audit. +``` + +## Examples + +### PostgreSQL with Default Settings + +```sh +goway -url postgres://user:password@localhost:5432/myapp migrate +``` + +### PostgreSQL with Custom Schema and Placeholders + +```sh +goway \ + -url postgres://localhost/myapp \ + -schemas app_schema,app_audit \ + -placeholders "env=production,region=us-east-1" \ + migrate +``` + +### SQLite with Target Version + +Apply migrations only up to version 1.2, leaving later migrations pending: + +```sh +goway -url sqlite:./app.db -target 1.2 migrate +``` + +### SQLite with Out-of-Order Support + +Enable out-of-order migration execution (useful in development): + +```sh +goway -url sqlite:./app.db -out-of-order=true migrate +``` + +### Using Environment Variables + +```sh +export GOWAY_URL=postgres://localhost/mydb +goway migrate +goway info +goway validate +``` + +### Custom History Table + +```sh +goway \ + -url postgres://localhost/mydb \ + -table schema_versions \ + migrate +``` + +### Baseline an Existing Database + +Record the current state at version 2.0 without running any migrations: + +```sh +goway \ + -url postgres://localhost/mydb \ + -baseline-version 2.0 \ + -baseline-description "Production cutover" \ + baseline +``` + +## Error Codes + +The CLI exits with code 1 on any error. Error messages are printed to stderr, structured as: + +``` +Error: +``` + +Common error conditions: +- Missing or invalid database URL +- Unsupported URL scheme (only `postgres://`, `postgresql://`, `sqlite:`, `file:`) +- No command specified or unknown command +- Validation failures +- Database connection errors +- Migration execution failures + +## Return Values + +| Command | Success | Returns | +|---------|---------|---------| +| `migrate` | Migrations applied or schema already up to date | 0 | +| `info` | Prints migration history and current version | 0 | +| `validate` | All migrations valid | 0 | +| `baseline` | Schema baselined or already baselined | 0 | +| `repair` | Repairs complete (may be 0 repairs) | 0 | +| `clean` | Schemas cleaned | 0 | +| Any error | Error message to stderr | 1 | + +:::tip +The CLI automatically detects the database dialect from the URL scheme, but you can use the Go API if you need to override or detect the dialect programmatically. See the [API reference](/goway/reference/) for details. +::: diff --git a/docs/src/content/docs/reference/commands.md b/docs/src/content/docs/reference/commands.md new file mode 100644 index 0000000..beeb5ac --- /dev/null +++ b/docs/src/content/docs/reference/commands.md @@ -0,0 +1,202 @@ +--- +title: Commands +description: Reference for migrate, info, validate, baseline, repair, and clean, with their result types. +--- + +All commands are methods on `*Migrator`, which is obtained by calling `Load()` on a configured `*Configuration`. Each command accepts a `context.Context` and returns a result struct and an error. + +## Migrate + +```go +func (f *Migrator) Migrate(ctx context.Context) (*MigrateResult, error) +``` + +Applies every pending migration in order, bringing the database up to date. Missing schemas and the schema history table are created when needed. Each migration runs inside its own transaction, so a failed migration leaves no partial state and can be retried after the script is corrected. + +Before applying migrations, `Migrate` will: +- Create any configured schemas that do not yet exist (unless schema creation is disabled). +- Create the schema history table if it does not exist. +- Optionally insert a baseline entry if configured with `BaselineOnMigrate(true)` and the history table is newly created. +- Run `beforeMigrate` callbacks. + +If `ValidateOnMigrate(true)` is set (the default), validation is performed before any migrations are applied. Validation failures cause `Migrate` to return without making changes. + +Each migration runs within a transaction on PostgreSQL, which is released after the migration completes. SQLite is single-writer by default. PostgreSQL enforces a session advisory lock to prevent concurrent migrators. + +The command fires `beforeEachMigrate` and `afterEachMigrate` callbacks around each migration, then runs `afterMigrate` callbacks once all migrations complete. + +### Result + +```go +type MigrateResult struct { + InitialSchemaVersion string // The schema version before migration + TargetSchemaVersion string // The schema version after migration + MigrationsExecuted int // Number of migrations applied + Migrations []MigrationInfo // Details of each applied migration +} +``` + +### Errors + +- `errors.Is(err, ErrValidationFailed)`: validation found a problem before applying any migrations. +- `errors.Is(err, ErrFailedMigration)`: a previously failed migration is present; resolve it with `Repair` before proceeding. +- Context cancellation or database errors. +- A migration may partially fail, in which case the result is returned with the target version as of the failure point. The failed row is recorded in the history table and can be repaired. + +## Info + +```go +func (f *Migrator) Info(ctx context.Context) (*InfoResult, error) +``` + +Computes and returns the state of every migration—both resolved and applied—without making any changes to the database. This is useful for auditing, logging, and integration with deployment tools. + +### Result + +```go +type InfoResult struct { + Migrations []MigrationInfo // Every migration, resolved and applied + Current string // The current schema version +} +``` + +## Validate + +```go +func (f *Migrator) Validate(ctx context.Context) (*ValidateResult, error) +``` + +Inspects the applied migrations against the resolved scripts and reports any discrepancies. Validation detects checksum mismatches, locally missing migrations, failed migrations, and unapplied migrations. + +If validation passes, the error is `nil` and `Valid` is `true`. If validation fails, an error wrapping `ErrValidationFailed` is returned, but the result still carries the full list of problems. + +### Result + +```go +type ValidateResult struct { + Valid bool // True if no problems were found + Errors []ValidationError // Details of each problem + ValidationCount int // Number of migrations inspected +} +``` + +### Errors + +- `errors.Is(err, ErrValidationFailed)`: one or more validation problems were found. + +## Baseline + +```go +func (f *Migrator) Baseline(ctx context.Context) (*BaselineResult, error) +``` + +Records a baseline entry in the schema history, allowing existing databases to be brought under migration management without re-running the migrations that produced their current state. Migrations at or below the baseline version are subsequently ignored. + +Baseline can only be called on an empty schema history (containing no applied migrations except synthetic entries like schema creation or a prior baseline). If the history already contains applied migrations, an error is returned. + +### Result + +```go +type BaselineResult struct { + BaselineVersion string // The version recorded as the baseline + BaselineDescription string // The description recorded as the baseline + Created bool // True if a new baseline entry was inserted; false if one already existed +} +``` + +## Repair + +```go +func (f *Migrator) Repair(ctx context.Context) (*RepairResult, error) +``` + +Removes failed entries from the schema history and realigns recorded checksums with resolved scripts. This is the recommended remedy after a failed migration on a database without transactional DDL, or after deliberately editing an already-applied migration script. + +Repair does not re-apply migrations; it only updates the history table. Use `Migrate` to apply pending or outdated migrations after repair. + +### Result + +```go +type RepairResult struct { + RemovedFailed int // Number of failed rows deleted from the history + AlignedChecksums int // Number of rows whose checksum was updated +} +``` + +## Clean + +```go +func (f *Migrator) Clean(ctx context.Context) (*CleanResult, error) +``` + +Drops every object in the managed schemas, returning the database to an empty state. This is a destructive and irreversible operation. Clean is disabled by default and must be explicitly enabled by calling `CleanDisabled(false)` on the configuration. + +### Result + +```go +type CleanResult struct { + SchemasCleaned []string // The schemas that were cleaned +} +``` + +### Errors + +- `errors.Is(err, ErrCleanDisabled)`: clean is disabled (the default). + +## Dialect + +```go +func (f *Migrator) Dialect() Dialect +``` + +Returns the database dialect that was configured or detected (e.g., PostgreSQL or SQLite). Useful for logging or adapting behavior based on dialect-specific capabilities. + +## Result Types + +### MigrationInfo + +```go +type MigrationInfo struct { + Version string // The version number (empty for repeatable migrations) + Description string // The migration description + Type string // The migration type: "SQL", "BASELINE", "SCHEMA", or "DELETED" + Script string // The script file name or synthetic marker + Checksum *int32 // CRC32 checksum of the resolved script; nil if not yet applied + State MigrationState // Current state of the migration + InstalledRank int // Database-assigned order; zero if not installed + InstalledOn *time.Time // Timestamp the migration was applied (database clock); nil if not installed + InstalledBy string // User or system that applied the migration; empty if not installed + ExecutionTime int // Duration in milliseconds; zero if not installed +} +``` + +### MigrationState + +A migration state is one of: + +- `"Pending"`: resolved but not yet applied. +- `"Success"`: applied successfully. +- `"Failed"`: previously applied but reported an error; must be repaired before migrating further. +- `"Out of Order"`: applied successfully but with a version lower than an already-applied migration (requires `OutOfOrder(true)` to apply). +- `"Outdated"`: a repeatable migration that has been re-applied with a different checksum. +- `"Superseded"`: a repeatable migration that has been replaced by a newer run. +- `"Missing"`: applied but no longer resolved locally. +- `"Failed (Missing)"`: applied but marked failed and no longer resolved locally. +- `"Future"`: applied with a version higher than any currently resolved migration. +- `"Failed (Future)"`: applied and failed, with a version higher than any currently resolved migration. +- `"Above Target"`: resolved but its version exceeds the configured target. +- `"Ignored"`: not applied; either at or below a baseline version or an out-of-order migration with `OutOfOrder(false)`. +- `"Baseline"`: a baseline entry in the history. + +### ValidationError + +```go +type ValidationError struct { + Version string // The version of the migration, empty for repeatable migrations + Description string // The migration description + Script string // The script file name + Message string // Explanation of the problem +} +``` + +Validation errors include checksum mismatches, missing applied migrations, failed migrations, and unapplied migrations (when called outside of `Migrate`). diff --git a/docs/src/content/docs/reference/configuration.md b/docs/src/content/docs/reference/configuration.md new file mode 100644 index 0000000..2ee261c --- /dev/null +++ b/docs/src/content/docs/reference/configuration.md @@ -0,0 +1,316 @@ +--- +title: Configuration +description: Complete reference for goway.Configure and every configuration setter. +--- + +The `Configuration` type collects all settings that control how migrations are discovered and applied. Create one with [`Configure()`](#configure), chain setter methods to customize behavior, and finalize it with [`Load()`](#load) or [`LoadContext()`](#loadcontext) to obtain a ready `Migrator`. + +## Configure + +```go +func Configure() *Configuration +``` + +Returns a new `Configuration` with the same defaults as Flyway. Every default is documented with its setter below. All setters return `*Configuration` for method chaining. + +## Load and LoadContext + +```go +func (c *Configuration) Load() (*Migrator, error) + +func (c *Configuration) LoadContext(ctx context.Context) (*Migrator, error) +``` + +Validates the configuration, auto-detects the dialect if one was not set explicitly, discovers migrations from the configured locations, and returns a ready `Migrator`. Errors include [`ErrNoDataSource`](/goway/reference/commands/), [`ErrNoDialect`](/goway/reference/commands/), and [`ErrUnsupportedDialect`](/goway/reference/commands/). + +`LoadContext` respects the supplied context during dialect detection. + +:::note +Configuration errors from invalid `BaselineVersion` or `Target` arguments surface at `Load` time, not when the setter was called. +::: + +## Configuration Setters + +### DataSource + +```go +func (c *Configuration) DataSource(db *sql.DB) *Configuration +``` + +Sets the database connection pool used for all operations. Required; `Load` returns [`ErrNoDataSource`](/goway/reference/commands/) if this is not set. + +**Default:** `nil` (required) + +### Dialect + +```go +func (c *Configuration) Dialect(dialect Dialect) *Configuration +``` + +Sets the database dialect explicitly. If not set, the dialect is auto-detected from the connection at `Load` time. Use `Postgres()` or `SQLite()` to create dialect instances. + +**Default:** `nil` (auto-detect) + +### Locations + +```go +func (c *Configuration) Locations(locations ...string) *Configuration +``` + +Replaces the list of file system directories scanned for migrations. Each location may carry a `"filesystem:"` or `"classpath:"` prefix; bare paths are also accepted. Locations are processed in order; the first matching migration found wins. + +**Default:** `[]string{"db/migration"}` + +### FS + +```go +func (c *Configuration) FS(fileSystem fs.FS, paths ...string) *Configuration +``` + +Registers an additional file system, such as one created by `go:embed`, together with the directory paths inside it that contain migrations. If no paths are provided, defaults to `"."`. Can be called multiple times to register multiple file systems. + +**Default:** (none; only filesystem locations are scanned unless this is called) + +### Schemas + +```go +func (c *Configuration) Schemas(schemas ...string) *Configuration +``` + +Sets the list of schemas managed by the migrator. The first schema is the default schema in which the schema history table is created (unless overridden by `DefaultSchema`). Schema management is only relevant for PostgreSQL; SQLite does not support schemas. + +**Default:** `nil` (no explicit schema list; the default schema is used) + +### DefaultSchema + +```go +func (c *Configuration) DefaultSchema(schema string) *Configuration +``` + +Overrides the schema in which the schema history table is created and managed. If not set, the first schema from `Schemas` is used, or the database default if no schemas are configured. + +**Default:** `""` (use first schema from `Schemas`, or the database default) + +### CreateSchemas + +```go +func (c *Configuration) CreateSchemas(create bool) *Configuration +``` + +Controls whether missing schemas are created automatically before migrations run. Only applicable on PostgreSQL. + +**Default:** `true` + +### Table + +```go +func (c *Configuration) Table(table string) *Configuration +``` + +Sets the name of the schema history table. The name should not be schema-qualified; the table is created in the default schema. + +**Default:** `"flyway_schema_history"` + +### BaselineVersion + +```go +func (c *Configuration) BaselineVersion(version string) *Configuration +``` + +Sets the version recorded by the `baseline` command and below which migrations are ignored. The version string is parsed and validated; invalid formats surface as an error from `Load`. Version format is a dot or underscore-separated sequence of integers with arbitrary precision. + +**Default:** `"1"` + +### BaselineDescription + +```go +func (c *Configuration) BaselineDescription(description string) *Configuration +``` + +Sets the description text recorded by the `baseline` command. + +**Default:** `"<< Flyway Baseline >>"` + +### BaselineOnMigrate + +```go +func (c *Configuration) BaselineOnMigrate(enabled bool) *Configuration +``` + +Controls whether a non-empty schema without a history table is baselined automatically on the first `migrate` command. + +**Default:** `false` + +### Placeholders + +```go +func (c *Configuration) Placeholders(placeholders map[string]string) *Configuration +``` + +Sets the placeholder key-value map. Keys are matched case-insensitively. Unresolved placeholders in migration scripts are errors. Built-in placeholders are always available: `${flyway:defaultSchema}`, `${flyway:user}`, `${flyway:table}`, `${flyway:filename}`. + +**Default:** `map[string]string{}` (empty; only built-ins available) + +### PlaceholderPrefix + +```go +func (c *Configuration) PlaceholderPrefix(prefix string) *Configuration +``` + +Sets the opening delimiter of a placeholder. + +**Default:** `"${"` + +### PlaceholderSuffix + +```go +func (c *Configuration) PlaceholderSuffix(suffix string) *Configuration +``` + +Sets the closing delimiter of a placeholder. + +**Default:** `"}"` + +### SQLMigrationPrefix + +```go +func (c *Configuration) SQLMigrationPrefix(prefix string) *Configuration +``` + +Sets the prefix that marks versioned migration scripts. Migration names match the pattern ``, e.g., `V1__init.sql`. + +**Default:** `"V"` + +### RepeatableSQLMigrationPrefix + +```go +func (c *Configuration) RepeatableSQLMigrationPrefix(prefix string) *Configuration +``` + +Sets the prefix that marks repeatable migration scripts. Repeatable migrations run after all versioned migrations, and are re-applied whenever their checksum changes. Migration names match the pattern `__`, e.g., `R__index_on_user_id.sql`. + +**Default:** `"R"` + +### SQLMigrationSeparator + +```go +func (c *Configuration) SQLMigrationSeparator(separator string) *Configuration +``` + +Sets the token between the version and description in versioned migration names. + +**Default:** `"__"` + +### SQLMigrationSuffixes + +```go +func (c *Configuration) SQLMigrationSuffixes(suffixes ...string) *Configuration +``` + +Sets the recognized file suffixes for migration scripts. Migration files matching any of these suffixes are considered. + +**Default:** `[]string{".sql"}` + +### ValidateOnMigrate + +```go +func (c *Configuration) ValidateOnMigrate(enabled bool) *Configuration +``` + +Controls whether the `migrate` command runs a validation check before applying migrations. If validation fails, `Migrate` returns an error and no migrations are applied. + +**Default:** `true` + +### CleanDisabled + +```go +func (c *Configuration) CleanDisabled(disabled bool) *Configuration +``` + +Controls whether the `clean` command is permitted. When `true`, calling `Clean` returns [`ErrCleanDisabled`](/goway/reference/commands/). Set to `false` only when you intentionally need to drop schema objects. + +**Default:** `true` (clean is disabled for safety) + +### OutOfOrder + +```go +func (c *Configuration) OutOfOrder(enabled bool) *Configuration +``` + +Controls whether migrations with a version lower than the current schema version may still be applied. When `false`, out-of-order migrations are an error. + +**Default:** `false` + +### Target + +```go +func (c *Configuration) Target(version string) *Configuration +``` + +Sets the highest version that `migrate` will apply. Migrations above the target are not executed. The version string is parsed and validated; invalid formats surface as an error from `Load`. + +**Default:** `nil` (no upper limit; all migrations are applied) + +### InstalledBy + +```go +func (c *Configuration) InstalledBy(user string) *Configuration +``` + +Overrides the user name recorded for each applied migration in the schema history table. + +**Default:** `""` (the database user is recorded) + +### Callbacks + +```go +func (c *Configuration) Callbacks(callbacks ...Callback) *Configuration +``` + +Registers programmatic callbacks invoked during a `migrate` run, in addition to SQL callback scripts discovered in the configured locations. See [Callbacks](/goway/reference/callbacks/) for event types and the callback interface. + +**Default:** (none) + +## Example + +```go +package main + +import ( + "context" + "database/sql" + "log" + + _ "github.com/jackc/pgx/v5/stdlib" + "github.com/cgardev/goway" +) + +func main() { + db, err := sql.Open("pgx", "postgres://user:password@localhost/mydb") + if err != nil { + log.Fatal(err) + } + + config := goway.Configure(). + DataSource(db). + Schemas("public", "data"). + DefaultSchema("public"). + Locations("filesystem:db/migrations", "filesystem:db/seeds"). + Placeholders(map[string]string{ + "schema_version": "1.0.0", + }). + ValidateOnMigrate(true) + + migrator, err := config.Load() + if err != nil { + log.Fatal(err) + } + + result, err := migrator.Migrate(context.Background()) + if err != nil { + log.Fatal(err) + } + + log.Printf("Applied %d migrations", result.MigrationsExecuted) +} +``` diff --git a/docs/src/content/docs/reference/dialects.md b/docs/src/content/docs/reference/dialects.md new file mode 100644 index 0000000..2477fe2 --- /dev/null +++ b/docs/src/content/docs/reference/dialects.md @@ -0,0 +1,184 @@ +--- +title: Dialects +description: The Dialect abstraction, Postgres() and SQLite(), detection, and per-dialect behavior. +--- + +A `Dialect` abstracts the differences between supported databases, allowing goway to work with multiple systems while handling database-specific SQL syntax, naming conventions, and features. + +The `Dialect` interface is sealed — it has no exported methods — so you obtain a dialect only through the constructor functions `Postgres()` and `SQLite()`. This ensures type safety and prevents accidental misuse. + +## Creating a Dialect + +```go +import "github.com/cgardev/goway" + +// Explicitly select PostgreSQL +dialect := goway.Postgres() + +// Explicitly select SQLite +dialect := goway.SQLite() +``` + +In most cases, you do not need to specify a dialect explicitly. When you call `Configure().Load()`, goway detects the dialect automatically by attempting to query `sqlite_version()` (unique to SQLite) and then `version()` (PostgreSQL). If neither query succeeds, `Load` returns `ErrNoDialect`; if a `version()` succeeds but does not identify PostgreSQL, it returns `ErrUnsupportedDialect`. + +You can override auto-detection with `Configure().Dialect(dialect)` if needed. + +## Retrieving the Active Dialect + +Once a `Migrator` is loaded, you can query which dialect is in use: + +```go +migrator, err := goway.Configure(). + DataSource(db). + Load(ctx) +if err != nil { + return err +} + +name := migrator.Dialect().Name() // "postgresql" or "sqlite" +``` + +## Supported Databases + +Goway targets the **two most recent major versions** of each database: + +- **PostgreSQL**: Current and previous major version (e.g., 16 and 15) +- **SQLite**: Current and previous major version (e.g., 3.46 and 3.45) + +:::note +Older versions may work, but compatibility is not guaranteed. +::: + +## Capabilities Matrix + +| Feature | PostgreSQL | SQLite | +|---------|-----------|--------| +| Addressable schemas | Yes | No | +| User identification | Yes | No | +| Advisory locks | Yes | No | +| Dollar-quoted string literals | Yes | No | +| BEGIN/CASE/END trigger blocks | No | Yes | +| Backtick and bracket identifiers | No | Yes | +| Multi-writer concurrency | Yes | Single writer | + +## PostgreSQL Dialect + +### Driver + +Use the pgx driver via `jackc/pgx/v5`: + +```go +import ( + "database/sql" + _ "github.com/jackc/pgx/v5/stdlib" +) + +db, err := sql.Open("pgx", "postgresql://user:password@localhost/dbname") +``` + +### Features + +**Schemas** + +PostgreSQL has addressable schemas. When you configure a default schema with [`DefaultSchema(name)`](/goway/reference/configuration/), goway creates it if it does not exist (controlled by [`CreateSchemas(bool)`](/goway/reference/configuration/)). The history table is placed in the default schema. + +**Search Path** + +For each transaction, goway executes `SET LOCAL search_path TO `, which makes the specified schema the default for unqualified object names within that transaction only. Non-transactional migrations (those marked with `-- goway:noTransaction`) use `SET search_path TO ` for the whole session. + +**Advisory Lock** + +Goway uses PostgreSQL's session advisory lock mechanism to prevent concurrent migrations. A single lock is held for the duration of the migration run. + +**Dollar-Quoted Strings** + +PostgreSQL's dollar-quoting syntax allows you to write functions and other bodies without escaping embedded quotes: + +```sql +-- V2__create_function.sql +CREATE FUNCTION add(a INT, b INT) RETURNS INT AS $$ +BEGIN + RETURN a + b; +END; +$$ LANGUAGE plpgsql; +``` + +The goway statement splitter understands dollar-quoted blocks and does not split within them. + +## SQLite Dialect + +### Driver + +Use the modernc.org/sqlite pure-Go driver: + +```go +import ( + "database/sql" + _ "modernc.org/sqlite" +) + +db, err := sql.Open("sqlite", "file:mydb.sqlite?cache=shared") +``` + +### Features + +**No Schemas** + +SQLite does not support addressable schemas. All tables exist in the main database. Configuration calls like `DefaultSchema()` and `Schemas()` have no effect. The history table is created in the main database. + +**Single Writer** + +SQLite enforces a single writer at a time. Concurrent migration runs from multiple processes will serialize and may timeout if locks are held too long. + +**Trigger Block Parsing** + +The statement splitter understands SQLite trigger block syntax and does not split on `BEGIN`, `CASE`, or `END` keywords when they appear inside trigger definitions: + +```sql +-- V3__create_trigger.sql +CREATE TRIGGER update_timestamp +AFTER UPDATE ON users +BEGIN + UPDATE users SET updated_at = CURRENT_TIMESTAMP + WHERE id = NEW.id; +END; +``` + +**Quoted Identifiers** + +SQLite accepts double quotes (standard SQL), backticks (MySQL compatibility), and square brackets (T-SQL compatibility): + +```sql +-- All three are valid in SQLite: +CREATE TABLE "users" (id INT); +CREATE TABLE `users` (id INT); +CREATE TABLE [users] (id INT); +``` + +Goway uses double quotes for generated SQL and accepts all three forms in migrations. + +## Error Handling + +If goway cannot detect a dialect, it returns `ErrNoDialect`: + +```go +if errors.Is(err, goway.ErrNoDialect) { + // Neither SQLite nor PostgreSQL detected; connection or query failed. +} +``` + +If auto-detection succeeds but identifies an unsupported database, it returns `ErrUnsupportedDialect`: + +```go +if errors.Is(err, goway.ErrUnsupportedDialect) { + // A database responded but was not PostgreSQL or SQLite. +} +``` + +Use `errors.Is()` rather than direct equality, since these errors are wrapped with context. + +## Next Steps + +- [Configuration](/goway/reference/configuration/) — Learn how to set default schemas, locations, placeholders, and other options. +- [Commands](/goway/reference/commands/) — Discover what each command does and what it returns. +- [Writing Migrations](/goway/guides/writing-migrations/) — See how to name and write migration scripts. diff --git a/docs/src/content/docs/reference/migration-naming.md b/docs/src/content/docs/reference/migration-naming.md new file mode 100644 index 0000000..9bbc6e8 --- /dev/null +++ b/docs/src/content/docs/reference/migration-naming.md @@ -0,0 +1,175 @@ +--- +title: Migration Naming +description: The exact rules for migration file names, versions, and ordering. +--- + +Goway migrations must follow a strict naming convention. File names encode the migration type (versioned or repeatable), version, and description. Understanding this system is essential for organizing your migrations and ensuring reliable execution. + +## File Name Components + +Every migration file name consists of these parts: + +1. **Prefix** — Identifies the migration type: + - `V` for versioned migrations (default) + - `R` for repeatable migrations +2. **Version** — For versioned migrations only; absent for repeatables +3. **Separator** — Divides version from description (default `__`) +4. **Description** — Human-readable text; underscores become spaces +5. **Suffix** — File extension; `.sql` by default + +### Example File Names + +- `V1__init.sql` — versioned migration 1, description "init" +- `V2.1__add_index.sql` — versioned migration 2.1, description "add index" +- `R__populate_constants.sql` — repeatable migration, description "populate constants" +- `V1_0__baseline.sql` — versioned migration 1.0 (underscores normalized to dots) + +## Configuring the Naming Convention + +Use fluent setters on the configuration object to adjust the naming rules: + +```go +config := goway.Configure(). + DataSource(db). + SQLMigrationPrefix("M"). // change versioned prefix + RepeatableSQLMigrationPrefix("RM"). // change repeatable prefix + SQLMigrationSeparator("--"). // change separator + SQLMigrationSuffixes(".sql", ".ddl") // add or replace suffixes +``` + +The defaults match Flyway: + +| Component | Default Value | +|-----------|---------------| +| Versioned prefix | `V` | +| Repeatable prefix | `R` | +| Separator | `__` | +| Suffixes | `.sql` | + +## Suffix Matching + +The suffix is matched **case-insensitively** against the file name, and the **first configured suffix wins**. This allows flexibility with file systems and editors: + +- `V1__init.SQL` matches `.sql` (case-insensitive) +- `V1__init.sql` and `V1__init.ddl` both match if suffixes are `[".sql", ".ddl"]` +- If suffixes are `[".ddl", ".sql"]`, a file named `V1__init.sql.ddl` matches `.ddl` first + +## Versioned Migrations + +A versioned migration has the form: + +``` +V__. +``` + +The version must be present and non-empty. Versions consist of one or more dot-separated non-negative integers. Underscores in the raw file name are normalized to dots during parsing. + +Valid examples: +- `V1__init.sql` +- `V1.0.1__schema_changes.sql` +- `V20260531__bootstrap.sql` (date-based version) +- `V1_0__init.sql` (underscores become dots: 1.0) + +Invalid examples: +- `V__init.sql` (missing version) +- `V1.2.X__changes.sql` (non-numeric component) +- `V1__init` (missing suffix) + +## Repeatable Migrations + +A repeatable migration has the form: + +``` +R__. +``` + +The version part must be absent. Repeatable migrations run after all versioned migrations and are re-applied whenever their content (and thus checksum) changes. + +Valid examples: +- `R__populate_constants.sql` +- `R__refresh_materialized_views.sql` + +Invalid examples: +- `R1__description.sql` (repeatable must not declare a version) +- `R__some_script` (missing suffix) + +## Version Parsing and Comparison + +Versions are parsed and compared numerically, enabling arbitrary-precision version numbers and intuitive ordering. + +### Parsing Rules + +1. Underscores in the raw file name are normalized to dots: `1_2_3` becomes `1.2.3` +2. The normalized string is split on dots to extract integer parts +3. Each part is parsed as a non-negative integer of arbitrary precision +4. Trailing zeros are dropped so that `1`, `1.0`, and `1.0.0` are equivalent + +### Comparison Rules + +Versions are compared element by element from left to right. Components beyond the length of either version are treated as zero, so `1` equals `1.0`. + +#### Comparison Examples + +| Version A | Version B | Comparison | Notes | +|-----------|-----------|------------|-------| +| 1 | 2 | A < B | Single-part versions | +| 1.0 | 1 | A = B | Trailing zeros dropped | +| 1.1 | 1.2 | A < B | Second component compared | +| 2.0.1 | 2.1 | A < B | 2.0.1 vs 2.1.0 (zero padding) | +| 10 | 9 | A > B | Numeric, not lexicographic | +| 20260531 | 20260601 | A < B | Date-based versions work | + +## Migration Ordering + +Goway resolves and applies pending migrations in this order: + +1. **Versioned migrations** — sorted by version in ascending order +2. **Repeatable migrations** — sorted by description in ascending (lexicographic) order + +For example, given these files: + +``` +V1__init.sql +V2.1__add_index.sql +R__populate_constants.sql +V2__baseline.sql +R__refresh_views.sql +``` + +The execution order would be: + +1. `V1__init.sql` (version 1) +2. `V2__baseline.sql` (version 2) +3. `V2.1__add_index.sql` (version 2.1) +4. `R__populate_constants.sql` (repeatable, description "populate constants") +5. `R__refresh_views.sql` (repeatable, description "refresh views") + +## Description Handling + +The description is everything between the separator and the suffix. All underscores in the description are converted to spaces before being stored in the schema history table. + +- File name: `V1__create_user_table.sql` +- Stored description: `create user table` + +Descriptions are case-sensitive for ordering repeatables (via lexicographic comparison) but are purely informational; they do not affect execution logic. + +## Duplicate Detection + +Goway rejects migrations with duplicate versions or duplicate repeatable descriptions, returning an error at load time. + +- **Duplicate versioned migration**: Two files targeting the same version (e.g., both `V1__init.sql` and `V1__baseline.sql`) return `ErrDuplicateVersion`. +- **Duplicate repeatable migration**: Two files with the same repeatable description (e.g., both `R__init_constants.sql` and `R__init_constants.sql`) return `ErrDuplicateRepeatable`. + +Version comparison ignores trailing zeros, so `V1.sql` and `V1.0.sql` are detected as duplicates. + +## Migration Checksums + +When a migration file is loaded, its content is checksummed with CRC32 (IEEE algorithm) for verification and change detection. The checksum: + +- Is computed **line by line**, excluding line terminators +- **Skips the BOM** on the first line if present +- Is **not affected** by placeholder substitution (computed on raw content) +- Is compared against the stored checksum in the schema history on subsequent runs +- Triggers a "state changed" error if it does not match for an already-applied migration + +This ensures Goway can reliably detect whether a migration has been modified after execution. diff --git a/docs/src/content/docs/reference/placeholders.md b/docs/src/content/docs/reference/placeholders.md new file mode 100644 index 0000000..45824fc --- /dev/null +++ b/docs/src/content/docs/reference/placeholders.md @@ -0,0 +1,127 @@ +--- +title: Placeholders +description: Placeholder substitution in migration scripts, delimiters, defaults, and built-ins. +--- + +Placeholders allow you to inject dynamic values into your migration scripts at execution time. goway supports the same placeholder syntax as Flyway, making it easy to maintain compatibility or migrate existing Flyway projects to Go. + +## Default delimiters + +Placeholders are delimited by `${` and `}` by default. Any placeholder found in a migration script or callback that does not have a configured value produces an error. + +```sql +-- Example migration using a placeholder +CREATE TABLE users ( + id BIGSERIAL PRIMARY KEY, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +GRANT SELECT ON users TO ${APP_DB_USER}; +``` + +## Configuring placeholder values + +Pass a map of key-value pairs to the `Placeholders` method during configuration: + +```go +migrator, err := goway.Configure(). + DataSource(db). + Placeholders(map[string]string{ + "APP_DB_USER": "app_reader", + "SCHEMA_NAME": "public", + }). + Load() +if err != nil { + log.Fatal(err) +} +``` + +Placeholder keys are matched case-insensitively. The configuration automatically stores keys in lowercase, so `${APP_DB_USER}` and `${app_db_user}` in your scripts will both match the configured key. + +## Custom delimiters + +You can change the opening and closing delimiters with `PlaceholderPrefix` and `PlaceholderSuffix`: + +```go +migrator, err := goway.Configure(). + DataSource(db). + PlaceholderPrefix("@"). + PlaceholderSuffix("@"). + Placeholders(map[string]string{ + "APP_USER": "app_service", + }). + Load() +if err != nil { + log.Fatal(err) +} +``` + +Migration scripts would then use `@APP_USER@` instead of `${APP_USER}`. + +## Built-in placeholders + +goway provides four built-in placeholders that are always available, regardless of what you configure: + +- `${flyway:defaultSchema}` — The default schema (the schema containing the schema history table) +- `${flyway:user}` — The name of the user that applied the migration (set by `InstalledBy`, defaults to the database connection user) +- `${flyway:table}` — The name of the schema history table +- `${flyway:filename}` — The name of the current migration script file + +:::tip +The `flyway:` prefix is kept for drop-in compatibility with Flyway. If you are migrating from Flyway, your existing placeholder scripts will work without modification. +::: + +Example using built-ins: + +```sql +-- V2__add_audit_log.sql +CREATE TABLE audit_log ( + event_id BIGSERIAL PRIMARY KEY, + event_timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + applied_by TEXT, + migration_script TEXT +); + +INSERT INTO ${flyway:defaultSchema}.audit_log (applied_by, migration_script) +VALUES ('${flyway:user}', '${flyway:filename}'); +``` + +## Timing and checksums + +Placeholder substitution happens at execution time, after migrations are loaded and resolved but before SQL statements are executed. Crucially, **placeholder substitution does not affect the migration checksum**. The checksum is computed on the raw file content before any placeholders are replaced. + +This means: +- You can change placeholder values between runs without invalidating the migration history +- A migration with the same script content will have the same checksum regardless of placeholder substitution +- The schema history table always records the filename, not the substituted result + +## Error handling + +An unresolved placeholder—one that appears in a script but has no value configured—produces an error before any SQL is executed: + +``` +goway: no value provided for placeholder ${UNDEFINED_VALUE} +``` + +Check that all placeholders in your scripts are listed in the configuration map, and that the keys match (case does not matter). + +## Placeholders in callbacks + +SQL callback scripts (found in your migration locations and named like `beforeMigrate.sql` or `afterEachMigrate__description.sql`) also support placeholder substitution. Built-in placeholders are available in callbacks, and any custom placeholders you configure work as well. + +:::note +Callback scripts use the same placeholder delimiters as regular migrations. If you customize `PlaceholderPrefix` or `PlaceholderSuffix`, both migrations and callbacks will use the new delimiters. +::: + +## Comparison to Flyway + +goway uses the same placeholder semantics as Flyway: + +- Default delimiters: `${` and `}` +- Built-in namespace: `flyway:` (e.g., `${flyway:defaultSchema}`) +- Case-insensitive key matching +- Substitution at execution time, not affecting checksums +- Unresolved placeholders produce errors +- Callbacks support placeholders + +If you are porting Flyway migrations to goway, you can reuse your placeholder configuration and scripts with no changes. diff --git a/docs/src/content/docs/reference/schema-history.md b/docs/src/content/docs/reference/schema-history.md new file mode 100644 index 0000000..8e2568d --- /dev/null +++ b/docs/src/content/docs/reference/schema-history.md @@ -0,0 +1,86 @@ +--- +title: Schema History +description: "The flyway_schema_history table: its columns, how rows are written, and how state is derived." +--- + +The schema history table tracks every migration applied to the database. goway creates and maintains this table automatically, storing all metadata needed to determine which migrations have run, in what order, and with what outcome. + +## The Schema History Table + +By default, goway writes to a table named `flyway_schema_history`. You can change this name with the [`Table()`](/goway/reference/configuration/) configuration method. The table always lives in the default schema. + +### Columns + +The schema history table has the following columns: + +| Column | Type | Meaning | +|--------|------|---------| +| `installed_rank` | INT | The numeric rank in which the migration was applied, starting at 1. Primary key. | +| `version` | VARCHAR(50) | The migration version, or NULL for repeatable migrations and schema markers. | +| `description` | VARCHAR(200) | The human-readable description of the migration. | +| `type` | VARCHAR(20) | The migration type: `SQL`, `BASELINE`, `SCHEMA`, or `DELETED`. | +| `script` | VARCHAR(1000) | The script file name or synthetic marker. | +| `checksum` | INTEGER | The CRC32 checksum of the script content, or NULL for synthetic entries. | +| `installed_by` | VARCHAR(100) | The database user that applied the migration. | +| `installed_on` | TIMESTAMP | The timestamp when the migration was applied. Set by the database. | +| `execution_time` | INTEGER | The duration of the migration in milliseconds. | +| `success` | BOOLEAN | Whether the migration completed successfully. | + +### Writing History + +Each time a migration executes, a row is inserted into the history table **in the same transaction as the migration itself**. This ensures that if the migration succeeds, the history row is committed together; if the migration fails, both are rolled back. + +The `installed_on` timestamp is set by the database server at insert time using the database's current time function: +- **PostgreSQL**: `now()` +- **SQLite**: `strftime('%Y-%m-%d %H:%M:%f', 'now')` + +The `installed_by` column records the database user who ran the migration. This is determined by querying the database (PostgreSQL's `current_user`, or an empty string on SQLite, which has no concept of user). You can override this with the [`InstalledBy()`](/goway/reference/configuration/) configuration method. + +:::note +Non-transactional migrations—those marked with `-- goway:noTransaction`—run outside a transaction and may fail without rolling back the history row. In this case, the row is marked with `success = false` and can be cleaned up with the [Repair](/goway/reference/commands/) command. +::: + +## Migration States + +The `MigrationState` describes the relationship between a resolved migration (found on disk) and a recorded history row. goway derives the state of each migration dynamically when you run [`Info`](/goway/reference/commands/), [`Validate`](/goway/reference/commands/), or [`Migrate`](/goway/reference/commands/). + +### Applied Migrations + +| State | Meaning | +|-------|---------| +| `Success` | A versioned or repeatable migration was applied successfully. | +| `Failed` | A migration was applied but failed and must be resolved. | +| `Outdated` | A repeatable migration was applied, but its script changed; it will be re-applied on the next migrate. | +| `Superseded` | An older run of a repeatable migration; a newer run of the same description is now current. | +| `Baseline` | The synthetic baseline entry created by the [`Baseline`](/goway/reference/commands/) command. | + +### Unapplied Migrations + +| State | Meaning | +|-------|---------| +| `Pending` | A resolved migration that has not been applied and will be applied on the next migrate. | +| `Above Target` | A pending migration with a version above the configured target version. | +| `Ignored` | A pending migration that will be skipped—for example, because it is below the baseline or (when out-of-order execution is disabled) because it was discovered after migrations of a higher version. | + +### Missing or Future Migrations + +| State | Meaning | +|-------|---------| +| `Missing` | A migration that was successfully applied in the past but is no longer resolved from the configured locations. | +| `Failed (Missing)` | A migration that failed in the past and is no longer resolved. | +| `Future` | An applied migration with a version higher than any currently resolved migration. | +| `Failed (Future)` | A failed applied migration with a version higher than any currently resolved migration. | +| `Out of Order` | A migration that was applied but whose version is earlier than other applied migrations (only shown when out-of-order execution is disabled). | + +## Repair and Maintenance + +The [`Repair`](/goway/reference/commands/) command helps recover from migration failures and script changes: + +- **Removes failed rows**: Deletes all history rows where `success = false`. This allows you to retry the migration after fixing the problem. +- **Realigns checksums**: Updates the recorded `checksum` of any applied migration to match the current resolved script. Use this after intentionally modifying an already-applied script (typically a repeatable migration). + +Repair does not modify successful migrations or change the order of migrations. Use the [`Clean`](/goway/reference/commands/) command if you need to remove all applied migrations entirely. + +## Flyway Compatibility + +The schema history table is designed for compatibility with [Flyway](https://flywaydb.org/). The table name, column names, type values (`SQL`, `BASELINE`, `SCHEMA`, `DELETED`), and placeholder names (`${flyway:user}`, `${flyway:table}`, etc.) follow Flyway conventions. This allows goway to read and write a history table that Flyway has already created, or for Flyway to continue using a history table that goway created. diff --git a/docs/src/styles/theme.css b/docs/src/styles/theme.css new file mode 100644 index 0000000..0d4dbf3 --- /dev/null +++ b/docs/src/styles/theme.css @@ -0,0 +1,100 @@ +/* + * Monochrome palette for the Starlight "Ion" theme. + * + * The Ion theme provides the layout and components; this file recolors it to a + * clean black-and-white palette by overriding Starlight's --sl-color-* custom + * properties, which Ion also reads. + * + * Selectors use a doubled :root so these declarations outrank both Starlight's + * defaults and Ion's stylesheet (which Starlight loads after this file), + * regardless of source order: + * :root:root -> dark mode (the default) + * :root:root[data-theme='light'] -> light mode (higher specificity, wins) + * Both blocks define the same token set, so no value leaks between modes. + * + * Palette: light bg #ffffff, text #0d0d0d, muted #676767, border #e5e5e5, + * surface #f7f7f8, accent near-black. + * dark bg #0d0d0d, text #ececec, muted #a1a1a1, border #2f2f2f, + * surface #171717, accent near-white. + */ + +/* Dark mode (default) */ +:root:root { + /* Base palette */ + --sl-color-white: #ececec; + --sl-color-gray-1: #ececec; + --sl-color-gray-2: #c5c5d2; + --sl-color-gray-3: #a1a1a1; + --sl-color-gray-4: #8e8e8e; + --sl-color-gray-5: #2f2f2f; + --sl-color-gray-6: #171717; + --sl-color-black: #0d0d0d; + + /* Neutral accent: links and the active item read as bright ink. */ + --sl-color-accent-low: #2f2f2f; + --sl-color-accent: #b4b4b4; + --sl-color-accent-high: #ffffff; + + /* Surfaces and borders for the dark scheme. */ + --sl-color-bg: #0d0d0d; + --sl-color-bg-nav: #0d0d0d; + --sl-color-bg-sidebar: #0d0d0d; + --sl-color-bg-inline-code: #171717; + --sl-color-hairline: #2f2f2f; + --sl-color-hairline-light: #2f2f2f; + --sl-color-hairline-shade: #000000; + --sl-color-text: #ececec; + --sl-color-text-accent: #ffffff; +} + +/* Light mode */ +:root:root[data-theme='light'] { + /* Base palette (inverted: gray-1 is the darkest ink in light mode) */ + --sl-color-white: #0d0d0d; + --sl-color-gray-1: #0d0d0d; + --sl-color-gray-2: #1a1a1a; + --sl-color-gray-3: #676767; + --sl-color-gray-4: #8e8e8e; + --sl-color-gray-5: #e5e5e5; + --sl-color-gray-6: #f7f7f8; + --sl-color-gray-7: #f7f7f8; + --sl-color-black: #ffffff; + + /* Neutral accent: links and the active item read as near-black ink. */ + --sl-color-accent-low: #e5e5e5; + --sl-color-accent: #676767; + --sl-color-accent-high: #0d0d0d; + + /* Surfaces and borders for the light scheme. */ + --sl-color-bg: #ffffff; + --sl-color-bg-nav: #ffffff; + --sl-color-bg-sidebar: #ffffff; + --sl-color-bg-inline-code: #f7f7f8; + --sl-color-hairline: #e5e5e5; + --sl-color-hairline-light: #ececec; + --sl-color-hairline-shade: #e5e5e5; + --sl-color-text: #0d0d0d; + --sl-color-text-accent: #0d0d0d; +} + +/* + * Home (splash) page width. + * + * On the splash template Starlight stretches the content to the full viewport, + * which reads as too wide on large screens. The splash .sl-container carries + * max-width: 100% !important, so it cannot be narrowed cleanly; instead each + * content block is capped to a centered column directly: the hero, the markdown + * body, and the page footer (which holds the prev/next pagination). Scoped to + * pages with a hero (data-has-hero, set by Starlight on the splash page), so the + * documentation pages keep their normal width. + */ +[data-has-hero] { + --sl-content-width: 60rem; +} + +[data-has-hero] .hero, +[data-has-hero] .sl-markdown-content, +[data-has-hero] .page-footer { + max-width: 60rem; + margin-inline: auto; +} diff --git a/docs/tsconfig.json b/docs/tsconfig.json new file mode 100644 index 0000000..8bf91d3 --- /dev/null +++ b/docs/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "astro/tsconfigs/strict", + "include": [".astro/types.d.ts", "**/*"], + "exclude": ["dist"] +}