diff --git a/.gitignore b/.gitignore
index 0a19790..fe8fc69 100644
--- a/.gitignore
+++ b/.gitignore
@@ -172,3 +172,13 @@ cython_debug/
# PyPI configuration file
.pypirc
+
+# GR-MCP project-specific
+examples/*_patched_*.py
+examples/*.wav
+tests/scratch/
+
+# Astro/Starlight docs site
+docs/.astro/
+docs/node_modules/
+docs/dist/
diff --git a/.mcp.json b/.mcp.json
new file mode 100644
index 0000000..def549b
--- /dev/null
+++ b/.mcp.json
@@ -0,0 +1,15 @@
+{
+ "mcpServers": {
+ "gnuradio-mcp": {
+ "type": "stdio",
+ "command": "uv",
+ "args": [
+ "run",
+ "--directory",
+ "/home/rpm/claude/sdr/gr-mcp",
+ "gnuradio-mcp"
+ ],
+ "env": {}
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs
new file mode 100644
index 0000000..352738e
--- /dev/null
+++ b/docs/astro.config.mjs
@@ -0,0 +1,48 @@
+import { defineConfig } from 'astro/config';
+import starlight from '@astrojs/starlight';
+
+export default defineConfig({
+ site: 'https://gr-mcp.supported.systems',
+ integrations: [
+ starlight({
+ title: 'GR-MCP',
+ description: 'GNU Radio MCP Server for programmatic flowgraph control',
+ social: {
+ github: 'https://git.supported.systems/MCP/gr-mcp',
+ },
+ editLink: {
+ baseUrl: 'https://git.supported.systems/MCP/gr-mcp/src/branch/main/docs/',
+ },
+ customCss: ['./src/styles/custom.css'],
+ sidebar: [
+ {
+ label: 'Getting Started',
+ autogenerate: { directory: 'getting-started' },
+ },
+ {
+ label: 'Guides',
+ autogenerate: { directory: 'guides' },
+ },
+ {
+ label: 'Reference',
+ items: [
+ { label: 'Tools Overview', link: '/reference/tools-overview/' },
+ {
+ label: 'Tool Reference',
+ collapsed: true,
+ autogenerate: { directory: 'reference/tools' },
+ },
+ { label: 'Docker Images', link: '/reference/docker-images/' },
+ { label: 'OOT Catalog', link: '/reference/oot-catalog/' },
+ ],
+ },
+ {
+ label: 'Concepts',
+ autogenerate: { directory: 'concepts' },
+ },
+ ],
+ }),
+ ],
+ telemetry: false,
+ devToolbar: { enabled: false },
+});
diff --git a/docs/package-lock.json b/docs/package-lock.json
new file mode 100644
index 0000000..aa1c2d2
--- /dev/null
+++ b/docs/package-lock.json
@@ -0,0 +1,6964 @@
+{
+ "name": "gr-mcp-docs",
+ "version": "0.0.1",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "gr-mcp-docs",
+ "version": "0.0.1",
+ "dependencies": {
+ "@astrojs/starlight": "^0.32.3",
+ "astro": "^5.2.5",
+ "sharp": "^0.33.5"
+ }
+ },
+ "node_modules/@astrojs/compiler": {
+ "version": "2.13.1",
+ "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.13.1.tgz",
+ "integrity": "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==",
+ "license": "MIT"
+ },
+ "node_modules/@astrojs/internal-helpers": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.5.tgz",
+ "integrity": "sha512-vreGnYSSKhAjFJCWAwe/CNhONvoc5lokxtRoZims+0wa3KbHBdPHSSthJsKxPd8d/aic6lWKpRTYGY/hsgK6EA==",
+ "license": "MIT"
+ },
+ "node_modules/@astrojs/markdown-remark": {
+ "version": "6.3.10",
+ "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.10.tgz",
+ "integrity": "sha512-kk4HeYR6AcnzC4QV8iSlOfh+N8TZ3MEStxPyenyCtemqn8IpEATBFMTJcfrNW32dgpt6MY3oCkMM/Tv3/I4G3A==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/internal-helpers": "0.7.5",
+ "@astrojs/prism": "3.3.0",
+ "github-slugger": "^2.0.0",
+ "hast-util-from-html": "^2.0.3",
+ "hast-util-to-text": "^4.0.2",
+ "import-meta-resolve": "^4.2.0",
+ "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",
+ "shiki": "^3.19.0",
+ "smol-toml": "^1.5.2",
+ "unified": "^11.0.5",
+ "unist-util-remove-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "unist-util-visit-parents": "^6.0.2",
+ "vfile": "^6.0.3"
+ }
+ },
+ "node_modules/@astrojs/mdx": {
+ "version": "4.3.13",
+ "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-4.3.13.tgz",
+ "integrity": "sha512-IHDHVKz0JfKBy3//52JSiyWv089b7GVSChIXLrlUOoTLWowG3wr2/8hkaEgEyd/vysvNQvGk+QhysXpJW5ve6Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/markdown-remark": "6.3.10",
+ "@mdx-js/mdx": "^3.1.1",
+ "acorn": "^8.15.0",
+ "es-module-lexer": "^1.7.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.0.0",
+ "vfile": "^6.0.3"
+ },
+ "engines": {
+ "node": "18.20.8 || ^20.3.0 || >=22.0.0"
+ },
+ "peerDependencies": {
+ "astro": "^5.0.0"
+ }
+ },
+ "node_modules/@astrojs/prism": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-3.3.0.tgz",
+ "integrity": "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "prismjs": "^1.30.0"
+ },
+ "engines": {
+ "node": "18.20.8 || ^20.3.0 || >=22.0.0"
+ }
+ },
+ "node_modules/@astrojs/sitemap": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.7.0.tgz",
+ "integrity": "sha512-+qxjUrz6Jcgh+D5VE1gKUJTA3pSthuPHe6Ao5JCxok794Lewx8hBFaWHtOnN0ntb2lfOf7gvOi9TefUswQ/ZVA==",
+ "license": "MIT",
+ "dependencies": {
+ "sitemap": "^8.0.2",
+ "stream-replace-string": "^2.0.0",
+ "zod": "^3.25.76"
+ }
+ },
+ "node_modules/@astrojs/starlight": {
+ "version": "0.32.6",
+ "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.32.6.tgz",
+ "integrity": "sha512-ASWGwNzq+0TmJ+GJFFxFFxx6Yra7BqIIMQbvOy/cweTHjqejB6mcaEWtS3Mag12LM7tXCES7v/fzmdPgjz8Yxw==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/mdx": "^4.0.5",
+ "@astrojs/sitemap": "^3.2.1",
+ "@pagefind/default-ui": "^1.3.0",
+ "@types/hast": "^3.0.4",
+ "@types/js-yaml": "^4.0.9",
+ "@types/mdast": "^4.0.4",
+ "astro-expressive-code": "^0.40.0",
+ "bcp-47": "^2.1.0",
+ "hast-util-from-html": "^2.0.1",
+ "hast-util-select": "^6.0.2",
+ "hast-util-to-string": "^3.0.0",
+ "hastscript": "^9.0.0",
+ "i18next": "^23.11.5",
+ "js-yaml": "^4.1.0",
+ "klona": "^2.0.6",
+ "mdast-util-directive": "^3.0.0",
+ "mdast-util-to-markdown": "^2.1.0",
+ "mdast-util-to-string": "^4.0.0",
+ "pagefind": "^1.3.0",
+ "rehype": "^13.0.1",
+ "rehype-format": "^5.0.0",
+ "remark-directive": "^3.0.0",
+ "unified": "^11.0.5",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.2"
+ },
+ "peerDependencies": {
+ "astro": "^5.1.5"
+ }
+ },
+ "node_modules/@astrojs/telemetry": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.0.tgz",
+ "integrity": "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ci-info": "^4.2.0",
+ "debug": "^4.4.0",
+ "dlv": "^1.1.3",
+ "dset": "^3.1.4",
+ "is-docker": "^3.0.0",
+ "is-wsl": "^3.1.0",
+ "which-pm-runs": "^1.1.0"
+ },
+ "engines": {
+ "node": "18.20.8 || ^20.3.0 || >=22.0.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz",
+ "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.0"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
+ "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
+ "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@capsizecss/unpack": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.0.tgz",
+ "integrity": "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==",
+ "license": "MIT",
+ "dependencies": {
+ "fontkitten": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@ctrl/tinycolor": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.2.0.tgz",
+ "integrity": "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
+ "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@expressive-code/core": {
+ "version": "0.40.2",
+ "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.40.2.tgz",
+ "integrity": "sha512-gXY3v7jbgz6nWKvRpoDxK4AHUPkZRuJsM79vHX/5uhV9/qX6Qnctp/U/dMHog/LCVXcuOps+5nRmf1uxQVPb3w==",
+ "license": "MIT",
+ "dependencies": {
+ "@ctrl/tinycolor": "^4.0.4",
+ "hast-util-select": "^6.0.2",
+ "hast-util-to-html": "^9.0.1",
+ "hast-util-to-text": "^4.0.1",
+ "hastscript": "^9.0.0",
+ "postcss": "^8.4.38",
+ "postcss-nested": "^6.0.1",
+ "unist-util-visit": "^5.0.0",
+ "unist-util-visit-parents": "^6.0.1"
+ }
+ },
+ "node_modules/@expressive-code/plugin-frames": {
+ "version": "0.40.2",
+ "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.40.2.tgz",
+ "integrity": "sha512-aLw5IlDlZWb10Jo/TTDCVsmJhKfZ7FJI83Zo9VDrV0OBlmHAg7klZqw68VDz7FlftIBVAmMby53/MNXPnMjTSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@expressive-code/core": "^0.40.2"
+ }
+ },
+ "node_modules/@expressive-code/plugin-shiki": {
+ "version": "0.40.2",
+ "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.40.2.tgz",
+ "integrity": "sha512-t2HMR5BO6GdDW1c1ISBTk66xO503e/Z8ecZdNcr6E4NpUfvY+MRje+LtrcvbBqMwWBBO8RpVKcam/Uy+1GxwKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@expressive-code/core": "^0.40.2",
+ "shiki": "^1.26.1"
+ }
+ },
+ "node_modules/@expressive-code/plugin-shiki/node_modules/@shikijs/core": {
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.29.2.tgz",
+ "integrity": "sha512-vju0lY9r27jJfOY4Z7+Rt/nIOjzJpZ3y+nYpqtUZInVoXQ/TJZcfGnNOGnKjFdVZb8qexiCuSlZRKcGfhhTTZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/engine-javascript": "1.29.2",
+ "@shikijs/engine-oniguruma": "1.29.2",
+ "@shikijs/types": "1.29.2",
+ "@shikijs/vscode-textmate": "^10.0.1",
+ "@types/hast": "^3.0.4",
+ "hast-util-to-html": "^9.0.4"
+ }
+ },
+ "node_modules/@expressive-code/plugin-shiki/node_modules/@shikijs/engine-javascript": {
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.29.2.tgz",
+ "integrity": "sha512-iNEZv4IrLYPv64Q6k7EPpOCE/nuvGiKl7zxdq0WFuRPF5PAE9PRo2JGq/d8crLusM59BRemJ4eOqrFrC4wiQ+A==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "1.29.2",
+ "@shikijs/vscode-textmate": "^10.0.1",
+ "oniguruma-to-es": "^2.2.0"
+ }
+ },
+ "node_modules/@expressive-code/plugin-shiki/node_modules/@shikijs/engine-oniguruma": {
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.29.2.tgz",
+ "integrity": "sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "1.29.2",
+ "@shikijs/vscode-textmate": "^10.0.1"
+ }
+ },
+ "node_modules/@expressive-code/plugin-shiki/node_modules/@shikijs/langs": {
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-1.29.2.tgz",
+ "integrity": "sha512-FIBA7N3LZ+223U7cJDUYd5shmciFQlYkFXlkKVaHsCPgfVLiO+e12FmQE6Tf9vuyEsFe3dIl8qGWKXgEHL9wmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "1.29.2"
+ }
+ },
+ "node_modules/@expressive-code/plugin-shiki/node_modules/@shikijs/themes": {
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-1.29.2.tgz",
+ "integrity": "sha512-i9TNZlsq4uoyqSbluIcZkmPL9Bfi3djVxRnofUHwvx/h6SRW3cwgBC5SML7vsDcWyukY0eCzVN980rqP6qNl9g==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "1.29.2"
+ }
+ },
+ "node_modules/@expressive-code/plugin-shiki/node_modules/@shikijs/types": {
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.29.2.tgz",
+ "integrity": "sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/vscode-textmate": "^10.0.1",
+ "@types/hast": "^3.0.4"
+ }
+ },
+ "node_modules/@expressive-code/plugin-shiki/node_modules/oniguruma-to-es": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-2.3.0.tgz",
+ "integrity": "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex-xs": "^1.0.0",
+ "regex": "^5.1.1",
+ "regex-recursion": "^5.1.1"
+ }
+ },
+ "node_modules/@expressive-code/plugin-shiki/node_modules/regex": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/regex/-/regex-5.1.1.tgz",
+ "integrity": "sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw==",
+ "license": "MIT",
+ "dependencies": {
+ "regex-utilities": "^2.3.0"
+ }
+ },
+ "node_modules/@expressive-code/plugin-shiki/node_modules/regex-recursion": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-5.1.1.tgz",
+ "integrity": "sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w==",
+ "license": "MIT",
+ "dependencies": {
+ "regex": "^5.1.1",
+ "regex-utilities": "^2.3.0"
+ }
+ },
+ "node_modules/@expressive-code/plugin-shiki/node_modules/shiki": {
+ "version": "1.29.2",
+ "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.29.2.tgz",
+ "integrity": "sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/core": "1.29.2",
+ "@shikijs/engine-javascript": "1.29.2",
+ "@shikijs/engine-oniguruma": "1.29.2",
+ "@shikijs/langs": "1.29.2",
+ "@shikijs/themes": "1.29.2",
+ "@shikijs/types": "1.29.2",
+ "@shikijs/vscode-textmate": "^10.0.1",
+ "@types/hast": "^3.0.4"
+ }
+ },
+ "node_modules/@expressive-code/plugin-text-markers": {
+ "version": "0.40.2",
+ "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.40.2.tgz",
+ "integrity": "sha512-/XoLjD67K9nfM4TgDlXAExzMJp6ewFKxNpfUw4F7q5Ecy+IU3/9zQQG/O70Zy+RxYTwKGw2MA9kd7yelsxnSmw==",
+ "license": "MIT",
+ "dependencies": {
+ "@expressive-code/core": "^0.40.2"
+ }
+ },
+ "node_modules/@img/colour": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz",
+ "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
+ "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz",
+ "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz",
+ "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz",
+ "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz",
+ "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz",
+ "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
+ "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-riscv64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
+ "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz",
+ "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz",
+ "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz",
+ "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz",
+ "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz",
+ "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.0.5"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz",
+ "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
+ "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-riscv64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
+ "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-riscv64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz",
+ "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz",
+ "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz",
+ "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz",
+ "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz",
+ "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.2.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
+ "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz",
+ "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz",
+ "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "license": "MIT"
+ },
+ "node_modules/@mdx-js/mdx": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz",
+ "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdx": "^2.0.0",
+ "acorn": "^8.0.0",
+ "collapse-white-space": "^2.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "estree-util-scope": "^1.0.0",
+ "estree-walker": "^3.0.0",
+ "hast-util-to-jsx-runtime": "^2.0.0",
+ "markdown-extensions": "^2.0.0",
+ "recma-build-jsx": "^1.0.0",
+ "recma-jsx": "^1.0.0",
+ "recma-stringify": "^1.0.0",
+ "rehype-recma": "^1.0.0",
+ "remark-mdx": "^3.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-rehype": "^11.0.0",
+ "source-map": "^0.7.0",
+ "unified": "^11.0.0",
+ "unist-util-position-from-estree": "^2.0.0",
+ "unist-util-stringify-position": "^4.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/@oslojs/encoding": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz",
+ "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==",
+ "license": "MIT"
+ },
+ "node_modules/@pagefind/darwin-arm64": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.4.0.tgz",
+ "integrity": "sha512-2vMqkbv3lbx1Awea90gTaBsvpzgRs7MuSgKDxW0m9oV1GPZCZbZBJg/qL83GIUEN2BFlY46dtUZi54pwH+/pTQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@pagefind/darwin-x64": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.4.0.tgz",
+ "integrity": "sha512-e7JPIS6L9/cJfow+/IAqknsGqEPjJnVXGjpGm25bnq+NPdoD3c/7fAwr1OXkG4Ocjx6ZGSCijXEV4ryMcH2E3A==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@pagefind/default-ui": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@pagefind/default-ui/-/default-ui-1.4.0.tgz",
+ "integrity": "sha512-wie82VWn3cnGEdIjh4YwNESyS1G6vRHwL6cNjy9CFgNnWW/PGRjsLq300xjVH5sfPFK3iK36UxvIBymtQIEiSQ==",
+ "license": "MIT"
+ },
+ "node_modules/@pagefind/freebsd-x64": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@pagefind/freebsd-x64/-/freebsd-x64-1.4.0.tgz",
+ "integrity": "sha512-WcJVypXSZ+9HpiqZjFXMUobfFfZZ6NzIYtkhQ9eOhZrQpeY5uQFqNWLCk7w9RkMUwBv1HAMDW3YJQl/8OqsV0Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@pagefind/linux-arm64": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.4.0.tgz",
+ "integrity": "sha512-PIt8dkqt4W06KGmQjONw7EZbhDF+uXI7i0XtRLN1vjCUxM9vGPdtJc2mUyVPevjomrGz5M86M8bqTr6cgDp1Uw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@pagefind/linux-x64": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.4.0.tgz",
+ "integrity": "sha512-z4oddcWwQ0UHrTHR8psLnVlz6USGJ/eOlDPTDYZ4cI8TK8PgwRUPQZp9D2iJPNIPcS6Qx/E4TebjuGJOyK8Mmg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@pagefind/windows-x64": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.4.0.tgz",
+ "integrity": "sha512-NkT+YAdgS2FPCn8mIA9bQhiBs+xmniMGq1LFPDhcFn0+2yIUEiIG06t7bsZlhdjknEQRTSdT7YitP6fC5qwP0g==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/pluginutils": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
+ "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^2.0.2",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/pluginutils/node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "license": "MIT"
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz",
+ "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz",
+ "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz",
+ "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz",
+ "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz",
+ "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz",
+ "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz",
+ "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz",
+ "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz",
+ "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz",
+ "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz",
+ "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz",
+ "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz",
+ "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz",
+ "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz",
+ "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz",
+ "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz",
+ "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz",
+ "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz",
+ "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz",
+ "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz",
+ "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz",
+ "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz",
+ "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz",
+ "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz",
+ "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@shikijs/core": {
+ "version": "3.22.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.22.0.tgz",
+ "integrity": "sha512-iAlTtSDDbJiRpvgL5ugKEATDtHdUVkqgHDm/gbD2ZS9c88mx7G1zSYjjOxp5Qa0eaW0MAQosFRmJSk354PRoQA==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "3.22.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4",
+ "hast-util-to-html": "^9.0.5"
+ }
+ },
+ "node_modules/@shikijs/engine-javascript": {
+ "version": "3.22.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.22.0.tgz",
+ "integrity": "sha512-jdKhfgW9CRtj3Tor0L7+yPwdG3CgP7W+ZEqSsojrMzCjD1e0IxIbwUMDDpYlVBlC08TACg4puwFGkZfLS+56Tw==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "3.22.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "oniguruma-to-es": "^4.3.4"
+ }
+ },
+ "node_modules/@shikijs/engine-oniguruma": {
+ "version": "3.22.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.22.0.tgz",
+ "integrity": "sha512-DyXsOG0vGtNtl7ygvabHd7Mt5EY8gCNqR9Y7Lpbbd/PbJvgWrqaKzH1JW6H6qFkuUa8aCxoiYVv8/YfFljiQxA==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "3.22.0",
+ "@shikijs/vscode-textmate": "^10.0.2"
+ }
+ },
+ "node_modules/@shikijs/langs": {
+ "version": "3.22.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.22.0.tgz",
+ "integrity": "sha512-x/42TfhWmp6H00T6uwVrdTJGKgNdFbrEdhaDwSR5fd5zhQ1Q46bHq9EO61SCEWJR0HY7z2HNDMaBZp8JRmKiIA==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "3.22.0"
+ }
+ },
+ "node_modules/@shikijs/themes": {
+ "version": "3.22.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.22.0.tgz",
+ "integrity": "sha512-o+tlOKqsr6FE4+mYJG08tfCFDS+3CG20HbldXeVoyP+cYSUxDhrFf3GPjE60U55iOkkjbpY2uC3It/eeja35/g==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "3.22.0"
+ }
+ },
+ "node_modules/@shikijs/types": {
+ "version": "3.22.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.22.0.tgz",
+ "integrity": "sha512-491iAekgKDBFE67z70Ok5a8KBMsQ2IJwOWw3us/7ffQkIBCyOQfm/aNwVMBUriP02QshIfgHCBSIYAl3u2eWjg==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4"
+ }
+ },
+ "node_modules/@shikijs/vscode-textmate": {
+ "version": "10.0.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz",
+ "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/debug": {
+ "version": "4.1.12",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
+ "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/ms": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "license": "MIT"
+ },
+ "node_modules/@types/estree-jsx": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz",
+ "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/@types/hast": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
+ "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/js-yaml": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz",
+ "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/mdast": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
+ "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/mdx": {
+ "version": "2.0.13",
+ "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz",
+ "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/ms": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/nlcst": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz",
+ "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "25.2.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.2.tgz",
+ "integrity": "sha512-BkmoP5/FhRYek5izySdkOneRyXYN35I860MFAGupTdebyE66uZaR+bXLHq8k4DirE5DwQi3NuhvRU1jqTVwUrQ==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.16.0"
+ }
+ },
+ "node_modules/@types/sax": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz",
+ "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/unist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
+ "license": "MIT"
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
+ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
+ "license": "ISC"
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ansi-align": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
+ "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.1.0"
+ }
+ },
+ "node_modules/ansi-align/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-align/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "license": "MIT"
+ },
+ "node_modules/ansi-align/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-align/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/anymatch/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/arg": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
+ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
+ "license": "MIT"
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "license": "Python-2.0"
+ },
+ "node_modules/aria-query": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
+ "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/array-iterate": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz",
+ "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/astring": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz",
+ "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==",
+ "license": "MIT",
+ "bin": {
+ "astring": "bin/astring"
+ }
+ },
+ "node_modules/astro": {
+ "version": "5.17.1",
+ "resolved": "https://registry.npmjs.org/astro/-/astro-5.17.1.tgz",
+ "integrity": "sha512-oD3tlxTaVWGq/Wfbqk6gxzVRz98xa/rYlpe+gU2jXJMSD01k6sEDL01ZlT8mVSYB/rMgnvIOfiQQ3BbLdN237A==",
+ "license": "MIT",
+ "dependencies": {
+ "@astrojs/compiler": "^2.13.0",
+ "@astrojs/internal-helpers": "0.7.5",
+ "@astrojs/markdown-remark": "6.3.10",
+ "@astrojs/telemetry": "3.3.0",
+ "@capsizecss/unpack": "^4.0.0",
+ "@oslojs/encoding": "^1.1.0",
+ "@rollup/pluginutils": "^5.3.0",
+ "acorn": "^8.15.0",
+ "aria-query": "^5.3.2",
+ "axobject-query": "^4.1.0",
+ "boxen": "8.0.1",
+ "ci-info": "^4.3.1",
+ "clsx": "^2.1.1",
+ "common-ancestor-path": "^1.0.1",
+ "cookie": "^1.1.1",
+ "cssesc": "^3.0.0",
+ "debug": "^4.4.3",
+ "deterministic-object-hash": "^2.0.2",
+ "devalue": "^5.6.2",
+ "diff": "^8.0.3",
+ "dlv": "^1.1.3",
+ "dset": "^3.1.4",
+ "es-module-lexer": "^1.7.0",
+ "esbuild": "^0.25.0",
+ "estree-walker": "^3.0.3",
+ "flattie": "^1.1.1",
+ "fontace": "~0.4.0",
+ "github-slugger": "^2.0.0",
+ "html-escaper": "3.0.3",
+ "http-cache-semantics": "^4.2.0",
+ "import-meta-resolve": "^4.2.0",
+ "js-yaml": "^4.1.1",
+ "magic-string": "^0.30.21",
+ "magicast": "^0.5.1",
+ "mrmime": "^2.0.1",
+ "neotraverse": "^0.6.18",
+ "p-limit": "^6.2.0",
+ "p-queue": "^8.1.1",
+ "package-manager-detector": "^1.6.0",
+ "piccolore": "^0.1.3",
+ "picomatch": "^4.0.3",
+ "prompts": "^2.4.2",
+ "rehype": "^13.0.2",
+ "semver": "^7.7.3",
+ "shiki": "^3.21.0",
+ "smol-toml": "^1.6.0",
+ "svgo": "^4.0.0",
+ "tinyexec": "^1.0.2",
+ "tinyglobby": "^0.2.15",
+ "tsconfck": "^3.1.6",
+ "ultrahtml": "^1.6.0",
+ "unifont": "~0.7.3",
+ "unist-util-visit": "^5.0.0",
+ "unstorage": "^1.17.4",
+ "vfile": "^6.0.3",
+ "vite": "^6.4.1",
+ "vitefu": "^1.1.1",
+ "xxhash-wasm": "^1.1.0",
+ "yargs-parser": "^21.1.1",
+ "yocto-spinner": "^0.2.3",
+ "zod": "^3.25.76",
+ "zod-to-json-schema": "^3.25.1",
+ "zod-to-ts": "^1.2.0"
+ },
+ "bin": {
+ "astro": "astro.js"
+ },
+ "engines": {
+ "node": "18.20.8 || ^20.3.0 || >=22.0.0",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/astrodotbuild"
+ },
+ "optionalDependencies": {
+ "sharp": "^0.34.0"
+ }
+ },
+ "node_modules/astro-expressive-code": {
+ "version": "0.40.2",
+ "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.40.2.tgz",
+ "integrity": "sha512-yJMQId0yXSAbW9I6yqvJ3FcjKzJ8zRL7elbJbllkv1ZJPlsI0NI83Pxn1YL1IapEM347EvOOkSW2GL+2+NO61w==",
+ "license": "MIT",
+ "dependencies": {
+ "rehype-expressive-code": "^0.40.2"
+ },
+ "peerDependencies": {
+ "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0"
+ }
+ },
+ "node_modules/astro/node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
+ "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.2.4"
+ }
+ },
+ "node_modules/astro/node_modules/@img/sharp-darwin-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
+ "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.2.4"
+ }
+ },
+ "node_modules/astro/node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
+ "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/astro/node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
+ "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/astro/node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
+ "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/astro/node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
+ "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/astro/node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
+ "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/astro/node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
+ "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/astro/node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
+ "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/astro/node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
+ "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/astro/node_modules/@img/sharp-linux-arm": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
+ "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.2.4"
+ }
+ },
+ "node_modules/astro/node_modules/@img/sharp-linux-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
+ "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.2.4"
+ }
+ },
+ "node_modules/astro/node_modules/@img/sharp-linux-s390x": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
+ "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.2.4"
+ }
+ },
+ "node_modules/astro/node_modules/@img/sharp-linux-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
+ "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.2.4"
+ }
+ },
+ "node_modules/astro/node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
+ "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
+ }
+ },
+ "node_modules/astro/node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
+ "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
+ }
+ },
+ "node_modules/astro/node_modules/@img/sharp-wasm32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
+ "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.7.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/astro/node_modules/@img/sharp-win32-ia32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
+ "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/astro/node_modules/@img/sharp-win32-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
+ "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/astro/node_modules/sharp": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
+ "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@img/colour": "^1.0.0",
+ "detect-libc": "^2.1.2",
+ "semver": "^7.7.3"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.34.5",
+ "@img/sharp-darwin-x64": "0.34.5",
+ "@img/sharp-libvips-darwin-arm64": "1.2.4",
+ "@img/sharp-libvips-darwin-x64": "1.2.4",
+ "@img/sharp-libvips-linux-arm": "1.2.4",
+ "@img/sharp-libvips-linux-arm64": "1.2.4",
+ "@img/sharp-libvips-linux-ppc64": "1.2.4",
+ "@img/sharp-libvips-linux-riscv64": "1.2.4",
+ "@img/sharp-libvips-linux-s390x": "1.2.4",
+ "@img/sharp-libvips-linux-x64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4",
+ "@img/sharp-linux-arm": "0.34.5",
+ "@img/sharp-linux-arm64": "0.34.5",
+ "@img/sharp-linux-ppc64": "0.34.5",
+ "@img/sharp-linux-riscv64": "0.34.5",
+ "@img/sharp-linux-s390x": "0.34.5",
+ "@img/sharp-linux-x64": "0.34.5",
+ "@img/sharp-linuxmusl-arm64": "0.34.5",
+ "@img/sharp-linuxmusl-x64": "0.34.5",
+ "@img/sharp-wasm32": "0.34.5",
+ "@img/sharp-win32-arm64": "0.34.5",
+ "@img/sharp-win32-ia32": "0.34.5",
+ "@img/sharp-win32-x64": "0.34.5"
+ }
+ },
+ "node_modules/axobject-query": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
+ "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/bail": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
+ "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/base-64": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz",
+ "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==",
+ "license": "MIT"
+ },
+ "node_modules/bcp-47": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-2.1.0.tgz",
+ "integrity": "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w==",
+ "license": "MIT",
+ "dependencies": {
+ "is-alphabetical": "^2.0.0",
+ "is-alphanumerical": "^2.0.0",
+ "is-decimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/bcp-47-match": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz",
+ "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+ "license": "ISC"
+ },
+ "node_modules/boxen": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz",
+ "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-align": "^3.0.1",
+ "camelcase": "^8.0.0",
+ "chalk": "^5.3.0",
+ "cli-boxes": "^3.0.0",
+ "string-width": "^7.2.0",
+ "type-fest": "^4.21.0",
+ "widest-line": "^5.0.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz",
+ "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ccount": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
+ "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "5.6.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
+ "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
+ "license": "MIT",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/character-entities": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
+ "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-html4": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
+ "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-legacy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-reference-invalid": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
+ "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
+ "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==",
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 20.19.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/ci-info": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz",
+ "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cli-boxes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
+ "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/collapse-white-space": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz",
+ "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/color": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
+ "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1",
+ "color-string": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=12.5.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "license": "MIT"
+ },
+ "node_modules/color-string": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+ "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
+ "node_modules/comma-separated-tokens": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
+ "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/commander": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
+ "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/common-ancestor-path": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz",
+ "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==",
+ "license": "ISC"
+ },
+ "node_modules/cookie": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
+ "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/cookie-es": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz",
+ "integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==",
+ "license": "MIT"
+ },
+ "node_modules/crossws": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz",
+ "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==",
+ "license": "MIT",
+ "dependencies": {
+ "uncrypto": "^0.1.3"
+ }
+ },
+ "node_modules/css-select": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
+ "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.1.0",
+ "domhandler": "^5.0.2",
+ "domutils": "^3.0.1",
+ "nth-check": "^2.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/css-selector-parser": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.3.0.tgz",
+ "integrity": "sha512-Y2asgMGFqJKF4fq4xHDSlFYIkeVfRsm69lQC1q9kbEsH5XtnINTMrweLkjYMeaUgiXBy/uvKeO/a1JHTNnmB2g==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/mdevils"
+ },
+ {
+ "type": "patreon",
+ "url": "https://patreon.com/mdevils"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/css-tree": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz",
+ "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==",
+ "license": "MIT",
+ "dependencies": {
+ "mdn-data": "2.12.2",
+ "source-map-js": "^1.0.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+ }
+ },
+ "node_modules/css-what": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
+ "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "license": "MIT",
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/csso": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
+ "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
+ "license": "MIT",
+ "dependencies": {
+ "css-tree": "~2.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/csso/node_modules/css-tree": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
+ "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
+ "license": "MIT",
+ "dependencies": {
+ "mdn-data": "2.0.28",
+ "source-map-js": "^1.0.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/csso/node_modules/mdn-data": {
+ "version": "2.0.28",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
+ "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
+ "license": "CC0-1.0"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decode-named-character-reference": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz",
+ "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/defu": {
+ "version": "6.1.4",
+ "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz",
+ "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
+ "license": "MIT"
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/destr": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz",
+ "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==",
+ "license": "MIT"
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/deterministic-object-hash": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/deterministic-object-hash/-/deterministic-object-hash-2.0.2.tgz",
+ "integrity": "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "base-64": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/devalue": {
+ "version": "5.6.2",
+ "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.2.tgz",
+ "integrity": "sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg==",
+ "license": "MIT"
+ },
+ "node_modules/devlop": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
+ "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/diff": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz",
+ "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/direction": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/direction/-/direction-2.0.1.tgz",
+ "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==",
+ "license": "MIT",
+ "bin": {
+ "direction": "cli.js"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/dlv": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+ "license": "MIT"
+ },
+ "node_modules/dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/dom-serializer/node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "domelementtype": "^2.3.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
+ "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/dset": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz",
+ "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "license": "MIT"
+ },
+ "node_modules/emoji-regex-xs": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz",
+ "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==",
+ "license": "MIT"
+ },
+ "node_modules/entities": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
+ "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+ "license": "MIT"
+ },
+ "node_modules/esast-util-from-estree": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz",
+ "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-visit": "^2.0.0",
+ "unist-util-position-from-estree": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/esast-util-from-js": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz",
+ "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "acorn": "^8.0.0",
+ "esast-util-from-estree": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/estree-util-attach-comments": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz",
+ "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-build-jsx": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz",
+ "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "estree-walker": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-is-identifier-name": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz",
+ "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-scope": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz",
+ "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-to-js": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz",
+ "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "astring": "^1.8.0",
+ "source-map": "^0.7.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-visit": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz",
+ "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
+ "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
+ "license": "MIT"
+ },
+ "node_modules/expressive-code": {
+ "version": "0.40.2",
+ "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.40.2.tgz",
+ "integrity": "sha512-1zIda2rB0qiDZACawzw2rbdBQiWHBT56uBctS+ezFe5XMAaFaHLnnSYND/Kd+dVzO9HfCXRDpzH3d+3fvOWRcw==",
+ "license": "MIT",
+ "dependencies": {
+ "@expressive-code/core": "^0.40.2",
+ "@expressive-code/plugin-frames": "^0.40.2",
+ "@expressive-code/plugin-shiki": "^0.40.2",
+ "@expressive-code/plugin-text-markers": "^0.40.2"
+ }
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "license": "MIT"
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/flattie": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz",
+ "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/fontace": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.1.tgz",
+ "integrity": "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==",
+ "license": "MIT",
+ "dependencies": {
+ "fontkitten": "^1.0.2"
+ }
+ },
+ "node_modules/fontkitten": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.2.tgz",
+ "integrity": "sha512-piJxbLnkD9Xcyi7dWJRnqszEURixe7CrF/efBfbffe2DPyabmuIuqraruY8cXTs19QoM8VJzx47BDRVNXETM7Q==",
+ "license": "MIT",
+ "dependencies": {
+ "tiny-inflate": "^1.0.3"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/get-east-asian-width": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz",
+ "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/github-slugger": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz",
+ "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==",
+ "license": "ISC"
+ },
+ "node_modules/h3": {
+ "version": "1.15.5",
+ "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.5.tgz",
+ "integrity": "sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg==",
+ "license": "MIT",
+ "dependencies": {
+ "cookie-es": "^1.2.2",
+ "crossws": "^0.3.5",
+ "defu": "^6.1.4",
+ "destr": "^2.0.5",
+ "iron-webcrypto": "^1.2.1",
+ "node-mock-http": "^1.0.4",
+ "radix3": "^1.1.2",
+ "ufo": "^1.6.3",
+ "uncrypto": "^0.1.3"
+ }
+ },
+ "node_modules/hast-util-embedded": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz",
+ "integrity": "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-is-element": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-format": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/hast-util-format/-/hast-util-format-1.1.0.tgz",
+ "integrity": "sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-embedded": "^3.0.0",
+ "hast-util-minify-whitespace": "^1.0.0",
+ "hast-util-phrasing": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "html-whitespace-sensitive-tag-names": "^3.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-from-html": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz",
+ "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "devlop": "^1.1.0",
+ "hast-util-from-parse5": "^8.0.0",
+ "parse5": "^7.0.0",
+ "vfile": "^6.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-from-parse5": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz",
+ "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "devlop": "^1.0.0",
+ "hastscript": "^9.0.0",
+ "property-information": "^7.0.0",
+ "vfile": "^6.0.0",
+ "vfile-location": "^5.0.0",
+ "web-namespaces": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-has-property": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz",
+ "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-is-body-ok-link": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-is-body-ok-link/-/hast-util-is-body-ok-link-3.0.1.tgz",
+ "integrity": "sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-is-element": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz",
+ "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-minify-whitespace": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-minify-whitespace/-/hast-util-minify-whitespace-1.0.1.tgz",
+ "integrity": "sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-embedded": "^3.0.0",
+ "hast-util-is-element": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-parse-selector": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz",
+ "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-phrasing": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-phrasing/-/hast-util-phrasing-3.0.1.tgz",
+ "integrity": "sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-embedded": "^3.0.0",
+ "hast-util-has-property": "^3.0.0",
+ "hast-util-is-body-ok-link": "^3.0.0",
+ "hast-util-is-element": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-raw": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz",
+ "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "hast-util-from-parse5": "^8.0.0",
+ "hast-util-to-parse5": "^8.0.0",
+ "html-void-elements": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "parse5": "^7.0.0",
+ "unist-util-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0",
+ "web-namespaces": "^2.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-select": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-6.0.4.tgz",
+ "integrity": "sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "bcp-47-match": "^2.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "css-selector-parser": "^3.0.0",
+ "devlop": "^1.0.0",
+ "direction": "^2.0.0",
+ "hast-util-has-property": "^3.0.0",
+ "hast-util-to-string": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "nth-check": "^2.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "unist-util-visit": "^5.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-estree": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz",
+ "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-attach-comments": "^3.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "mdast-util-mdx-expression": "^2.0.0",
+ "mdast-util-mdx-jsx": "^3.0.0",
+ "mdast-util-mdxjs-esm": "^2.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "style-to-js": "^1.0.0",
+ "unist-util-position": "^5.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-html": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz",
+ "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "html-void-elements": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "stringify-entities": "^4.0.0",
+ "zwitch": "^2.0.4"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-jsx-runtime": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz",
+ "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "mdast-util-mdx-expression": "^2.0.0",
+ "mdast-util-mdx-jsx": "^3.0.0",
+ "mdast-util-mdxjs-esm": "^2.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "style-to-js": "^1.0.0",
+ "unist-util-position": "^5.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-parse5": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz",
+ "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "devlop": "^1.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "web-namespaces": "^2.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-string": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz",
+ "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-text": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz",
+ "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "hast-util-is-element": "^3.0.0",
+ "unist-util-find-after": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-whitespace": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
+ "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hastscript": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz",
+ "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "hast-util-parse-selector": "^4.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/html-escaper": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz",
+ "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==",
+ "license": "MIT"
+ },
+ "node_modules/html-void-elements": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz",
+ "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/html-whitespace-sensitive-tag-names": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/html-whitespace-sensitive-tag-names/-/html-whitespace-sensitive-tag-names-3.0.1.tgz",
+ "integrity": "sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/http-cache-semantics": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
+ "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/i18next": {
+ "version": "23.16.8",
+ "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.8.tgz",
+ "integrity": "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://locize.com"
+ },
+ {
+ "type": "individual",
+ "url": "https://locize.com/i18next.html"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.23.2"
+ }
+ },
+ "node_modules/import-meta-resolve": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz",
+ "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/inline-style-parser": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz",
+ "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==",
+ "license": "MIT"
+ },
+ "node_modules/iron-webcrypto": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz",
+ "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/brc-dd"
+ }
+ },
+ "node_modules/is-alphabetical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
+ "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-alphanumerical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
+ "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
+ "license": "MIT",
+ "dependencies": {
+ "is-alphabetical": "^2.0.0",
+ "is-decimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz",
+ "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==",
+ "license": "MIT"
+ },
+ "node_modules/is-decimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
+ "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-docker": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
+ "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
+ "license": "MIT",
+ "bin": {
+ "is-docker": "cli.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-hexadecimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
+ "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-inside-container": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
+ "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
+ "license": "MIT",
+ "dependencies": {
+ "is-docker": "^3.0.0"
+ },
+ "bin": {
+ "is-inside-container": "cli.js"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-wsl": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz",
+ "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==",
+ "license": "MIT",
+ "dependencies": {
+ "is-inside-container": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/kleur": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
+ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/klona": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz",
+ "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/longest-streak": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
+ "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "11.2.5",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz",
+ "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/magicast": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz",
+ "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.0",
+ "@babel/types": "^7.29.0",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "node_modules/markdown-extensions": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz",
+ "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/markdown-table": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
+ "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/mdast-util-definitions": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz",
+ "integrity": "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "unist-util-visit": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-directive": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.1.0.tgz",
+ "integrity": "sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "parse-entities": "^4.0.0",
+ "stringify-entities": "^4.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-find-and-replace": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
+ "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "escape-string-regexp": "^5.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-from-markdown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
+ "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark": "^4.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz",
+ "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==",
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-gfm-autolink-literal": "^2.0.0",
+ "mdast-util-gfm-footnote": "^2.0.0",
+ "mdast-util-gfm-strikethrough": "^2.0.0",
+ "mdast-util-gfm-table": "^2.0.0",
+ "mdast-util-gfm-task-list-item": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-autolink-literal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz",
+ "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-find-and-replace": "^3.0.0",
+ "micromark-util-character": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-footnote": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz",
+ "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-strikethrough": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz",
+ "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-table": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz",
+ "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "markdown-table": "^3.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-task-list-item": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz",
+ "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz",
+ "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==",
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-mdx-expression": "^2.0.0",
+ "mdast-util-mdx-jsx": "^3.0.0",
+ "mdast-util-mdxjs-esm": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx-expression": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
+ "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx-jsx": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz",
+ "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "parse-entities": "^4.0.0",
+ "stringify-entities": "^4.0.0",
+ "unist-util-stringify-position": "^4.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdxjs-esm": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz",
+ "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-phrasing": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
+ "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-hast": {
+ "version": "13.2.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz",
+ "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "trim-lines": "^3.0.0",
+ "unist-util-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-markdown": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz",
+ "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "longest-streak": "^3.0.0",
+ "mdast-util-phrasing": "^4.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "unist-util-visit": "^5.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
+ "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdn-data": {
+ "version": "2.12.2",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz",
+ "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==",
+ "license": "CC0-1.0"
+ },
+ "node_modules/micromark": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
+ "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/debug": "^4.0.0",
+ "debug": "^4.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-core-commonmark": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz",
+ "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-destination": "^2.0.0",
+ "micromark-factory-label": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-factory-title": "^2.0.0",
+ "micromark-factory-whitespace": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-html-tag-name": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-directive": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz",
+ "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-factory-whitespace": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "parse-entities": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
+ "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-extension-gfm-autolink-literal": "^2.0.0",
+ "micromark-extension-gfm-footnote": "^2.0.0",
+ "micromark-extension-gfm-strikethrough": "^2.0.0",
+ "micromark-extension-gfm-table": "^2.0.0",
+ "micromark-extension-gfm-tagfilter": "^2.0.0",
+ "micromark-extension-gfm-task-list-item": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-autolink-literal": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
+ "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-footnote": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
+ "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-strikethrough": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz",
+ "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-table": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz",
+ "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-tagfilter": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz",
+ "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-task-list-item": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz",
+ "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-mdx-expression": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz",
+ "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-mdx-expression": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-mdx-jsx": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz",
+ "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "micromark-factory-mdx-expression": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-mdx-md": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz",
+ "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-mdxjs": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz",
+ "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==",
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.0.0",
+ "acorn-jsx": "^5.0.0",
+ "micromark-extension-mdx-expression": "^3.0.0",
+ "micromark-extension-mdx-jsx": "^3.0.0",
+ "micromark-extension-mdx-md": "^2.0.0",
+ "micromark-extension-mdxjs-esm": "^3.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-mdxjs-esm": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz",
+ "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-position-from-estree": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-factory-destination": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
+ "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-label": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz",
+ "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-mdx-expression": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz",
+ "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-position-from-estree": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ }
+ },
+ "node_modules/micromark-factory-space": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
+ "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-title": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz",
+ "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-whitespace": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz",
+ "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-character": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-chunked": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz",
+ "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-classify-character": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz",
+ "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-combine-extensions": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz",
+ "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-numeric-character-reference": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz",
+ "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-string": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz",
+ "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-encode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
+ "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-events-to-acorn": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz",
+ "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/unist": "^3.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-visit": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ }
+ },
+ "node_modules/micromark-util-html-tag-name": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz",
+ "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-normalize-identifier": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz",
+ "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-resolve-all": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz",
+ "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-sanitize-uri": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
+ "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-subtokenize": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz",
+ "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-types": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz",
+ "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/mrmime": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
+ "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/neotraverse": {
+ "version": "0.6.18",
+ "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.18.tgz",
+ "integrity": "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/nlcst-to-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz",
+ "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/nlcst": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/node-fetch-native": {
+ "version": "1.6.7",
+ "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz",
+ "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==",
+ "license": "MIT"
+ },
+ "node_modules/node-mock-http": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.4.tgz",
+ "integrity": "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==",
+ "license": "MIT"
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/nth-check?sponsor=1"
+ }
+ },
+ "node_modules/ofetch": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz",
+ "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==",
+ "license": "MIT",
+ "dependencies": {
+ "destr": "^2.0.5",
+ "node-fetch-native": "^1.6.7",
+ "ufo": "^1.6.1"
+ }
+ },
+ "node_modules/ohash": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz",
+ "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==",
+ "license": "MIT"
+ },
+ "node_modules/oniguruma-parser": {
+ "version": "0.12.1",
+ "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz",
+ "integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==",
+ "license": "MIT"
+ },
+ "node_modules/oniguruma-to-es": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.4.tgz",
+ "integrity": "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==",
+ "license": "MIT",
+ "dependencies": {
+ "oniguruma-parser": "^0.12.1",
+ "regex": "^6.0.1",
+ "regex-recursion": "^6.0.2"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.2.0.tgz",
+ "integrity": "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==",
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-queue": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.1.tgz",
+ "integrity": "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==",
+ "license": "MIT",
+ "dependencies": {
+ "eventemitter3": "^5.0.1",
+ "p-timeout": "^6.1.2"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-timeout": {
+ "version": "6.1.4",
+ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz",
+ "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/package-manager-detector": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz",
+ "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==",
+ "license": "MIT"
+ },
+ "node_modules/pagefind": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.4.0.tgz",
+ "integrity": "sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g==",
+ "license": "MIT",
+ "bin": {
+ "pagefind": "lib/runner/bin.cjs"
+ },
+ "optionalDependencies": {
+ "@pagefind/darwin-arm64": "1.4.0",
+ "@pagefind/darwin-x64": "1.4.0",
+ "@pagefind/freebsd-x64": "1.4.0",
+ "@pagefind/linux-arm64": "1.4.0",
+ "@pagefind/linux-x64": "1.4.0",
+ "@pagefind/windows-x64": "1.4.0"
+ }
+ },
+ "node_modules/parse-entities": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz",
+ "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "character-entities-legacy": "^3.0.0",
+ "character-reference-invalid": "^2.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "is-alphanumerical": "^2.0.0",
+ "is-decimal": "^2.0.0",
+ "is-hexadecimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/parse-entities/node_modules/@types/unist": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+ "license": "MIT"
+ },
+ "node_modules/parse-latin": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz",
+ "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/nlcst": "^2.0.0",
+ "@types/unist": "^3.0.0",
+ "nlcst-to-string": "^4.0.0",
+ "unist-util-modify-children": "^4.0.0",
+ "unist-util-visit-children": "^3.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/parse5": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
+ "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+ "license": "MIT",
+ "dependencies": {
+ "entities": "^6.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/piccolore": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz",
+ "integrity": "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==",
+ "license": "ISC"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-nested": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
+ "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^6.1.1"
+ },
+ "engines": {
+ "node": ">=12.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.14"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
+ "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/prismjs": {
+ "version": "1.30.0",
+ "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
+ "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/prompts": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
+ "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
+ "license": "MIT",
+ "dependencies": {
+ "kleur": "^3.0.3",
+ "sisteransi": "^1.0.5"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/property-information": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz",
+ "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/radix3": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz",
+ "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==",
+ "license": "MIT"
+ },
+ "node_modules/readdirp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz",
+ "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20.19.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/recma-build-jsx": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz",
+ "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-util-build-jsx": "^3.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/recma-jsx": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz",
+ "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==",
+ "license": "MIT",
+ "dependencies": {
+ "acorn-jsx": "^5.0.0",
+ "estree-util-to-js": "^2.0.0",
+ "recma-parse": "^1.0.0",
+ "recma-stringify": "^1.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ },
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/recma-parse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz",
+ "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "esast-util-from-js": "^2.0.0",
+ "unified": "^11.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/recma-stringify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz",
+ "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-util-to-js": "^2.0.0",
+ "unified": "^11.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/regex": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz",
+ "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==",
+ "license": "MIT",
+ "dependencies": {
+ "regex-utilities": "^2.3.0"
+ }
+ },
+ "node_modules/regex-recursion": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz",
+ "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==",
+ "license": "MIT",
+ "dependencies": {
+ "regex-utilities": "^2.3.0"
+ }
+ },
+ "node_modules/regex-utilities": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz",
+ "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==",
+ "license": "MIT"
+ },
+ "node_modules/rehype": {
+ "version": "13.0.2",
+ "resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.2.tgz",
+ "integrity": "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "rehype-parse": "^9.0.0",
+ "rehype-stringify": "^10.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-expressive-code": {
+ "version": "0.40.2",
+ "resolved": "https://registry.npmjs.org/rehype-expressive-code/-/rehype-expressive-code-0.40.2.tgz",
+ "integrity": "sha512-+kn+AMGCrGzvtH8Q5lC6Y5lnmTV/r33fdmi5QU/IH1KPHKobKr5UnLwJuqHv5jBTSN/0v2wLDS7RTM73FVzqmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "expressive-code": "^0.40.2"
+ }
+ },
+ "node_modules/rehype-format": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/rehype-format/-/rehype-format-5.0.1.tgz",
+ "integrity": "sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-format": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-parse": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz",
+ "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-from-html": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-raw": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz",
+ "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-raw": "^9.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-recma": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz",
+ "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "hast-util-to-estree": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-stringify": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz",
+ "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-to-html": "^9.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-directive": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.1.tgz",
+ "integrity": "sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-directive": "^3.0.0",
+ "micromark-extension-directive": "^3.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-gfm": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
+ "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-gfm": "^3.0.0",
+ "micromark-extension-gfm": "^3.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-stringify": "^11.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-mdx": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz",
+ "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==",
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-mdx": "^3.0.0",
+ "micromark-extension-mdxjs": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-parse": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
+ "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-rehype": {
+ "version": "11.1.2",
+ "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz",
+ "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "unified": "^11.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-smartypants": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.2.tgz",
+ "integrity": "sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==",
+ "license": "MIT",
+ "dependencies": {
+ "retext": "^9.0.0",
+ "retext-smartypants": "^6.0.0",
+ "unified": "^11.0.4",
+ "unist-util-visit": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/remark-stringify": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
+ "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/retext": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz",
+ "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/nlcst": "^2.0.0",
+ "retext-latin": "^4.0.0",
+ "retext-stringify": "^4.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/retext-latin": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz",
+ "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/nlcst": "^2.0.0",
+ "parse-latin": "^7.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/retext-smartypants": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz",
+ "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/nlcst": "^2.0.0",
+ "nlcst-to-string": "^4.0.0",
+ "unist-util-visit": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/retext-stringify": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz",
+ "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/nlcst": "^2.0.0",
+ "nlcst-to-string": "^4.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.57.1",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz",
+ "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.57.1",
+ "@rollup/rollup-android-arm64": "4.57.1",
+ "@rollup/rollup-darwin-arm64": "4.57.1",
+ "@rollup/rollup-darwin-x64": "4.57.1",
+ "@rollup/rollup-freebsd-arm64": "4.57.1",
+ "@rollup/rollup-freebsd-x64": "4.57.1",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.57.1",
+ "@rollup/rollup-linux-arm-musleabihf": "4.57.1",
+ "@rollup/rollup-linux-arm64-gnu": "4.57.1",
+ "@rollup/rollup-linux-arm64-musl": "4.57.1",
+ "@rollup/rollup-linux-loong64-gnu": "4.57.1",
+ "@rollup/rollup-linux-loong64-musl": "4.57.1",
+ "@rollup/rollup-linux-ppc64-gnu": "4.57.1",
+ "@rollup/rollup-linux-ppc64-musl": "4.57.1",
+ "@rollup/rollup-linux-riscv64-gnu": "4.57.1",
+ "@rollup/rollup-linux-riscv64-musl": "4.57.1",
+ "@rollup/rollup-linux-s390x-gnu": "4.57.1",
+ "@rollup/rollup-linux-x64-gnu": "4.57.1",
+ "@rollup/rollup-linux-x64-musl": "4.57.1",
+ "@rollup/rollup-openbsd-x64": "4.57.1",
+ "@rollup/rollup-openharmony-arm64": "4.57.1",
+ "@rollup/rollup-win32-arm64-msvc": "4.57.1",
+ "@rollup/rollup-win32-ia32-msvc": "4.57.1",
+ "@rollup/rollup-win32-x64-gnu": "4.57.1",
+ "@rollup/rollup-win32-x64-msvc": "4.57.1",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/sax": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz",
+ "integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=11.0.0"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/sharp": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz",
+ "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "color": "^4.2.3",
+ "detect-libc": "^2.0.3",
+ "semver": "^7.6.3"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.33.5",
+ "@img/sharp-darwin-x64": "0.33.5",
+ "@img/sharp-libvips-darwin-arm64": "1.0.4",
+ "@img/sharp-libvips-darwin-x64": "1.0.4",
+ "@img/sharp-libvips-linux-arm": "1.0.5",
+ "@img/sharp-libvips-linux-arm64": "1.0.4",
+ "@img/sharp-libvips-linux-s390x": "1.0.4",
+ "@img/sharp-libvips-linux-x64": "1.0.4",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.0.4",
+ "@img/sharp-libvips-linuxmusl-x64": "1.0.4",
+ "@img/sharp-linux-arm": "0.33.5",
+ "@img/sharp-linux-arm64": "0.33.5",
+ "@img/sharp-linux-s390x": "0.33.5",
+ "@img/sharp-linux-x64": "0.33.5",
+ "@img/sharp-linuxmusl-arm64": "0.33.5",
+ "@img/sharp-linuxmusl-x64": "0.33.5",
+ "@img/sharp-wasm32": "0.33.5",
+ "@img/sharp-win32-ia32": "0.33.5",
+ "@img/sharp-win32-x64": "0.33.5"
+ }
+ },
+ "node_modules/shiki": {
+ "version": "3.22.0",
+ "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.22.0.tgz",
+ "integrity": "sha512-LBnhsoYEe0Eou4e1VgJACes+O6S6QC0w71fCSp5Oya79inkwkm15gQ1UF6VtQ8j/taMDh79hAB49WUk8ALQW3g==",
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/core": "3.22.0",
+ "@shikijs/engine-javascript": "3.22.0",
+ "@shikijs/engine-oniguruma": "3.22.0",
+ "@shikijs/langs": "3.22.0",
+ "@shikijs/themes": "3.22.0",
+ "@shikijs/types": "3.22.0",
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4"
+ }
+ },
+ "node_modules/simple-swizzle": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz",
+ "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==",
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.3.1"
+ }
+ },
+ "node_modules/sisteransi": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+ "license": "MIT"
+ },
+ "node_modules/sitemap": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-8.0.2.tgz",
+ "integrity": "sha512-LwktpJcyZDoa0IL6KT++lQ53pbSrx2c9ge41/SeLTyqy2XUNA6uR4+P9u5IVo5lPeL2arAcOKn1aZAxoYbCKlQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "^17.0.5",
+ "@types/sax": "^1.2.1",
+ "arg": "^5.0.0",
+ "sax": "^1.4.1"
+ },
+ "bin": {
+ "sitemap": "dist/cli.js"
+ },
+ "engines": {
+ "node": ">=14.0.0",
+ "npm": ">=6.0.0"
+ }
+ },
+ "node_modules/sitemap/node_modules/@types/node": {
+ "version": "17.0.45",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz",
+ "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==",
+ "license": "MIT"
+ },
+ "node_modules/smol-toml": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.0.tgz",
+ "integrity": "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/cyyynthia"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.7.6",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz",
+ "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/space-separated-tokens": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
+ "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/stream-replace-string": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz",
+ "integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==",
+ "license": "MIT"
+ },
+ "node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/stringify-entities": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
+ "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities-html4": "^2.0.0",
+ "character-entities-legacy": "^3.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+ "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/style-to-js": {
+ "version": "1.1.21",
+ "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz",
+ "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==",
+ "license": "MIT",
+ "dependencies": {
+ "style-to-object": "1.0.14"
+ }
+ },
+ "node_modules/style-to-object": {
+ "version": "1.0.14",
+ "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz",
+ "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==",
+ "license": "MIT",
+ "dependencies": {
+ "inline-style-parser": "0.2.7"
+ }
+ },
+ "node_modules/svgo": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.0.tgz",
+ "integrity": "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==",
+ "license": "MIT",
+ "dependencies": {
+ "commander": "^11.1.0",
+ "css-select": "^5.1.0",
+ "css-tree": "^3.0.1",
+ "css-what": "^6.1.0",
+ "csso": "^5.0.5",
+ "picocolors": "^1.1.1",
+ "sax": "^1.4.1"
+ },
+ "bin": {
+ "svgo": "bin/svgo.js"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/svgo"
+ }
+ },
+ "node_modules/tiny-inflate": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
+ "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==",
+ "license": "MIT"
+ },
+ "node_modules/tinyexec": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
+ "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/trim-lines": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
+ "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/trough": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
+ "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/tsconfck": {
+ "version": "3.1.6",
+ "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz",
+ "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==",
+ "license": "MIT",
+ "bin": {
+ "tsconfck": "bin/tsconfck.js"
+ },
+ "engines": {
+ "node": "^18 || >=20"
+ },
+ "peerDependencies": {
+ "typescript": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD",
+ "optional": true
+ },
+ "node_modules/type-fest": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
+ "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/ufo": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz",
+ "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==",
+ "license": "MIT"
+ },
+ "node_modules/ultrahtml": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.6.0.tgz",
+ "integrity": "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==",
+ "license": "MIT"
+ },
+ "node_modules/uncrypto": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz",
+ "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==",
+ "license": "MIT"
+ },
+ "node_modules/undici-types": {
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+ "license": "MIT"
+ },
+ "node_modules/unified": {
+ "version": "11.0.5",
+ "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
+ "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "bail": "^2.0.0",
+ "devlop": "^1.0.0",
+ "extend": "^3.0.0",
+ "is-plain-obj": "^4.0.0",
+ "trough": "^2.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unifont": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.3.tgz",
+ "integrity": "sha512-b0GtQzKCyuSHGsfj5vyN8st7muZ6VCI4XD4vFlr7Uy1rlWVYxC3npnfk8MyreHxJYrz1ooLDqDzFe9XqQTlAhA==",
+ "license": "MIT",
+ "dependencies": {
+ "css-tree": "^3.1.0",
+ "ofetch": "^1.5.1",
+ "ohash": "^2.0.11"
+ }
+ },
+ "node_modules/unist-util-find-after": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz",
+ "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-is": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz",
+ "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-modify-children": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz",
+ "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "array-iterate": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-position": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
+ "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-position-from-estree": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz",
+ "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-remove-position": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz",
+ "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-visit": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-stringify-position": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
+ "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz",
+ "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit-children": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz",
+ "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit-parents": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz",
+ "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unstorage": {
+ "version": "1.17.4",
+ "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.4.tgz",
+ "integrity": "sha512-fHK0yNg38tBiJKp/Vgsq4j0JEsCmgqH58HAn707S7zGkArbZsVr/CwINoi+nh3h98BRCwKvx1K3Xg9u3VV83sw==",
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "^3.1.3",
+ "chokidar": "^5.0.0",
+ "destr": "^2.0.5",
+ "h3": "^1.15.5",
+ "lru-cache": "^11.2.0",
+ "node-fetch-native": "^1.6.7",
+ "ofetch": "^1.5.1",
+ "ufo": "^1.6.3"
+ },
+ "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
+ }
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "license": "MIT"
+ },
+ "node_modules/vfile": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
+ "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-location": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz",
+ "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-message": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz",
+ "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vite": {
+ "version": "6.4.1",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
+ "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.3",
+ "rollup": "^4.34.9",
+ "tinyglobby": "^0.2.13"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "jiti": ">=1.21.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "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
+ }
+ }
+ },
+ "node_modules/vitefu": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.1.tgz",
+ "integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==",
+ "license": "MIT",
+ "workspaces": [
+ "tests/deps/*",
+ "tests/projects/*",
+ "tests/projects/workspace/packages/*"
+ ],
+ "peerDependencies": {
+ "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0"
+ },
+ "peerDependenciesMeta": {
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/web-namespaces": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
+ "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/which-pm-runs": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz",
+ "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/widest-line": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz",
+ "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==",
+ "license": "MIT",
+ "dependencies": {
+ "string-width": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/xxhash-wasm": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz",
+ "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==",
+ "license": "MIT"
+ },
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz",
+ "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/yocto-spinner": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/yocto-spinner/-/yocto-spinner-0.2.3.tgz",
+ "integrity": "sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==",
+ "license": "MIT",
+ "dependencies": {
+ "yoctocolors": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=18.19"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/yoctocolors": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz",
+ "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zod": {
+ "version": "3.25.76",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
+ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-to-json-schema": {
+ "version": "3.25.1",
+ "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz",
+ "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==",
+ "license": "ISC",
+ "peerDependencies": {
+ "zod": "^3.25 || ^4"
+ }
+ },
+ "node_modules/zod-to-ts": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/zod-to-ts/-/zod-to-ts-1.2.0.tgz",
+ "integrity": "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==",
+ "peerDependencies": {
+ "typescript": "^4.9.4 || ^5.0.2",
+ "zod": "^3"
+ }
+ },
+ "node_modules/zwitch": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
+ "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ }
+ }
+}
diff --git a/docs/package.json b/docs/package.json
new file mode 100644
index 0000000..b30c7e3
--- /dev/null
+++ b/docs/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "gr-mcp-docs",
+ "type": "module",
+ "version": "0.0.1",
+ "scripts": {
+ "dev": "astro dev",
+ "start": "astro dev",
+ "build": "astro build",
+ "preview": "astro preview",
+ "astro": "astro",
+ "generate-api-docs": "python scripts/generate-api-docs.py"
+ },
+ "dependencies": {
+ "@astrojs/starlight": "^0.32.3",
+ "astro": "^5.2.5",
+ "sharp": "^0.33.5"
+ }
+}
diff --git a/docs/scripts/generate-api-docs.py b/docs/scripts/generate-api-docs.py
new file mode 100644
index 0000000..de64ee1
--- /dev/null
+++ b/docs/scripts/generate-api-docs.py
@@ -0,0 +1,355 @@
+#!/usr/bin/env python3
+"""Generate MDX documentation from GR-MCP tool docstrings.
+
+Usage:
+ cd docs
+ python scripts/generate-api-docs.py
+
+This script introspects the PlatformProvider and RuntimeProvider classes
+to extract tool signatures and docstrings, then generates MDX files for
+the Starlight documentation site.
+"""
+
+import inspect
+import sys
+from pathlib import Path
+from typing import get_type_hints
+
+# Add project root to path
+PROJECT_ROOT = Path(__file__).parent.parent.parent
+sys.path.insert(0, str(PROJECT_ROOT))
+
+# Import providers
+from gnuradio_mcp.providers.base import PlatformProvider
+from gnuradio_mcp.providers.runtime import RuntimeProvider
+
+OUTPUT_DIR = Path(__file__).parent.parent / "src/content/docs/reference/tools"
+
+# Tool categorization
+TOOL_CATEGORIES = {
+ "flowgraph": {
+ "title": "Flowgraph Tools",
+ "description": "Tools for managing flowgraph structure: blocks, connections, save/load.",
+ "tools": [
+ "get_blocks",
+ "make_block",
+ "remove_block",
+ "save_flowgraph",
+ "load_flowgraph",
+ "get_flowgraph_options",
+ "set_flowgraph_options",
+ "export_flowgraph_data",
+ "import_flowgraph_data",
+ ],
+ },
+ "blocks": {
+ "title": "Block Tools",
+ "description": "Tools for block parameter and port management.",
+ "tools": [
+ "get_block_params",
+ "set_block_params",
+ "get_block_sources",
+ "get_block_sinks",
+ "bypass_block",
+ "unbypass_block",
+ ],
+ },
+ "connections": {
+ "title": "Connection Tools",
+ "description": "Tools for wiring blocks together.",
+ "tools": [
+ "get_connections",
+ "connect_blocks",
+ "disconnect_blocks",
+ ],
+ },
+ "validation": {
+ "title": "Validation Tools",
+ "description": "Tools for checking flowgraph validity.",
+ "tools": [
+ "validate_block",
+ "validate_flowgraph",
+ "get_all_errors",
+ ],
+ },
+ "platform": {
+ "title": "Platform Tools",
+ "description": "Tools for discovering available blocks and managing OOT paths.",
+ "tools": [
+ "get_all_available_blocks",
+ "search_blocks",
+ "get_block_categories",
+ "load_oot_blocks",
+ "add_block_path",
+ "get_block_paths",
+ ],
+ },
+ "codegen": {
+ "title": "Code Generation",
+ "description": "Tools for generating Python code and evaluating expressions.",
+ "tools": [
+ "generate_code",
+ "evaluate_expression",
+ "create_embedded_python_block",
+ ],
+ },
+ "runtime-mode": {
+ "title": "Runtime Mode",
+ "description": "Tools for enabling/disabling runtime features and checking client capabilities.",
+ "tools": [
+ "get_runtime_mode",
+ "enable_runtime_mode",
+ "disable_runtime_mode",
+ "get_client_capabilities",
+ "list_client_roots",
+ ],
+ },
+ "docker": {
+ "title": "Docker Tools",
+ "description": "Tools for container lifecycle management.",
+ "tools": [
+ "launch_flowgraph",
+ "list_containers",
+ "stop_flowgraph",
+ "remove_flowgraph",
+ "capture_screenshot",
+ "get_container_logs",
+ ],
+ },
+ "xmlrpc": {
+ "title": "XML-RPC Tools",
+ "description": "Tools for XML-RPC connection and variable control.",
+ "tools": [
+ "connect",
+ "connect_to_container",
+ "disconnect",
+ "get_status",
+ "list_variables",
+ "get_variable",
+ "set_variable",
+ "start",
+ "stop",
+ "lock",
+ "unlock",
+ ],
+ },
+ "controlport": {
+ "title": "ControlPort Tools",
+ "description": "Tools for ControlPort/Thrift connection and monitoring.",
+ "tools": [
+ "connect_controlport",
+ "connect_to_container_controlport",
+ "disconnect_controlport",
+ "get_knobs",
+ "set_knobs",
+ "get_knob_properties",
+ "get_performance_counters",
+ "post_message",
+ ],
+ },
+ "coverage": {
+ "title": "Coverage Tools",
+ "description": "Tools for collecting Python code coverage from containers.",
+ "tools": [
+ "collect_coverage",
+ "generate_coverage_report",
+ "combine_coverage",
+ "delete_coverage",
+ ],
+ },
+ "oot": {
+ "title": "OOT Tools",
+ "description": "Tools for OOT module detection and installation.",
+ "tools": [
+ "detect_oot_modules",
+ "install_oot_module",
+ "list_oot_images",
+ "remove_oot_image",
+ "build_multi_oot_image",
+ "list_combo_images",
+ "remove_combo_image",
+ ],
+ },
+}
+
+
+def get_method_info(method):
+ """Extract signature and docstring from a method."""
+ sig = inspect.signature(method)
+ doc = inspect.getdoc(method) or "No description available."
+
+ # Parse docstring sections
+ lines = doc.split("\n")
+ description = []
+ args = []
+ returns = ""
+ example = []
+
+ section = "description"
+ for line in lines:
+ stripped = line.strip()
+ if stripped.startswith("Args:"):
+ section = "args"
+ continue
+ elif stripped.startswith("Returns:"):
+ section = "returns"
+ continue
+ elif stripped.startswith("Example:") or stripped.startswith("Examples:"):
+ section = "example"
+ continue
+
+ if section == "description":
+ description.append(line)
+ elif section == "args":
+ args.append(line)
+ elif section == "returns":
+ returns += line + "\n"
+ elif section == "example":
+ example.append(line)
+
+ # Get parameter info from signature
+ params = []
+ for name, param in sig.parameters.items():
+ if name == "self":
+ continue
+ param_type = ""
+ if param.annotation != inspect.Parameter.empty:
+ param_type = str(param.annotation).replace("typing.", "")
+ default = ""
+ if param.default != inspect.Parameter.empty:
+ default = repr(param.default)
+ params.append({
+ "name": name,
+ "type": param_type,
+ "default": default,
+ })
+
+ # Get return type
+ return_type = ""
+ try:
+ hints = get_type_hints(method)
+ if "return" in hints:
+ return_type = str(hints["return"]).replace("typing.", "")
+ except Exception:
+ pass
+
+ return {
+ "name": method.__name__,
+ "description": "\n".join(description).strip(),
+ "params": params,
+ "returns": returns.strip(),
+ "return_type": return_type,
+ "args_doc": "\n".join(args).strip(),
+ "example": "\n".join(example).strip(),
+ }
+
+
+def get_all_methods():
+ """Get all tool methods from both providers."""
+ methods = {}
+
+ # Get PlatformProvider methods
+ for name, method in inspect.getmembers(PlatformProvider, predicate=inspect.isfunction):
+ if not name.startswith("_"):
+ methods[name] = get_method_info(method)
+
+ # Get RuntimeProvider methods
+ for name, method in inspect.getmembers(RuntimeProvider, predicate=inspect.isfunction):
+ if not name.startswith("_"):
+ methods[name] = get_method_info(method)
+
+ return methods
+
+
+def generate_mdx(category_key: str, category: dict, methods: dict) -> str:
+ """Generate MDX content for a category."""
+ lines = [
+ "---",
+ f'title: {category["title"]}',
+ f'description: {category["description"]}',
+ "---",
+ "",
+ f'{category["description"]}',
+ "",
+ ]
+
+ for tool_name in category["tools"]:
+ if tool_name not in methods:
+ lines.append(f"## `{tool_name}`")
+ lines.append("")
+ lines.append("*Documentation pending.*")
+ lines.append("")
+ continue
+
+ info = methods[tool_name]
+
+ lines.append(f"## `{tool_name}`")
+ lines.append("")
+ lines.append(info["description"])
+ lines.append("")
+
+ # Parameters table
+ if info["params"]:
+ lines.append("### Parameters")
+ lines.append("")
+ lines.append("| Name | Type | Default | Description |")
+ lines.append("|------|------|---------|-------------|")
+ for param in info["params"]:
+ default = param["default"] if param["default"] else "-"
+ ptype = param["type"] if param["type"] else "-"
+ # Extract description from args_doc if available
+ desc = "-"
+ if info["args_doc"]:
+ for arg_line in info["args_doc"].split("\n"):
+ if arg_line.strip().startswith(f"{param['name']}:"):
+ desc = arg_line.split(":", 1)[1].strip()
+ break
+ lines.append(f"| `{param['name']}` | `{ptype}` | `{default}` | {desc} |")
+ lines.append("")
+
+ # Returns
+ if info["returns"] or info["return_type"]:
+ lines.append("### Returns")
+ lines.append("")
+ if info["return_type"]:
+ lines.append(f"**Type:** `{info['return_type']}`")
+ lines.append("")
+ if info["returns"]:
+ lines.append(info["returns"])
+ lines.append("")
+
+ # Example
+ if info["example"]:
+ lines.append("### Example")
+ lines.append("")
+ lines.append("```python")
+ lines.append(info["example"])
+ lines.append("```")
+ lines.append("")
+
+ lines.append("---")
+ lines.append("")
+
+ return "\n".join(lines)
+
+
+def main():
+ OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
+
+ print("Extracting methods from providers...")
+ methods = get_all_methods()
+ print(f"Found {len(methods)} methods")
+
+ for category_key, category in TOOL_CATEGORIES.items():
+ print(f"Generating {category_key}.mdx...")
+ content = generate_mdx(category_key, category, methods)
+ output_path = OUTPUT_DIR / f"{category_key}.mdx"
+ output_path.write_text(content)
+ print(f" Wrote {output_path}")
+
+ print("\nDone! Generated MDX files in:")
+ print(f" {OUTPUT_DIR}")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/docs/src/content.config.ts b/docs/src/content.config.ts
new file mode 100644
index 0000000..ab4db09
--- /dev/null
+++ b/docs/src/content.config.ts
@@ -0,0 +1,10 @@
+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/concepts/architecture.mdx b/docs/src/content/docs/concepts/architecture.mdx
new file mode 100644
index 0000000..b881037
--- /dev/null
+++ b/docs/src/content/docs/concepts/architecture.mdx
@@ -0,0 +1,237 @@
+---
+title: Architecture
+description: GR-MCP system architecture and design principles
+draft: false
+---
+
+import { Aside } from '@astrojs/starlight/components';
+
+GR-MCP follows a **Middleware + Provider** pattern that abstracts GNU Radio's internal
+objects into clean, serializable models suitable for the MCP protocol.
+
+## High-Level Overview
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ MCP Client │
+│ (Claude, Cursor, etc.) │
+└─────────────────────────────────────────────────────────────────┘
+ │
+ │ MCP Protocol (stdio/SSE)
+ ▼
+┌─────────────────────────────────────────────────────────────────┐
+│ FastMCP App │
+│ (main.py) │
+└─────────────────────────────────────────────────────────────────┘
+ │
+ ┌───────────────┴───────────────┐
+ ▼ ▼
+┌─────────────────────────┐ ┌─────────────────────────────────┐
+│ McpPlatformProvider │ │ McpRuntimeProvider │
+│ (29 tools) │ │ (5 always + ~40 dynamic) │
+│ │ │ │
+│ • get_blocks │ │ Always: │
+│ • make_block │ │ • get_runtime_mode │
+│ • connect_blocks │ │ • enable_runtime_mode │
+│ • validate_flowgraph │ │ • disable_runtime_mode │
+│ • generate_code │ │ • get_client_capabilities │
+│ • ... │ │ • list_client_roots │
+│ │ │ │
+│ │ │ Dynamic (when enabled): │
+│ │ │ • launch_flowgraph │
+│ │ │ • connect_to_container │
+│ │ │ • set_variable │
+│ │ │ • ... │
+└─────────────────────────┘ └─────────────────────────────────┘
+ │ │
+ ▼ ▼
+┌─────────────────────────┐ ┌─────────────────────────────────┐
+│ PlatformProvider │ │ RuntimeProvider │
+│ (Business Logic) │ │ (Business Logic) │
+└─────────────────────────┘ └─────────────────────────────────┘
+ │ │
+ ▼ ▼
+┌─────────────────────────┐ ┌─────────────────────────────────┐
+│ Middlewares │ │ Middlewares │
+│ │ │ │
+│ • PlatformMiddleware │ │ • DockerMiddleware │
+│ • FlowGraphMiddleware │ │ • XmlRpcMiddleware │
+│ • BlockMiddleware │ │ • ThriftMiddleware │
+│ │ │ • OOTInstallerMiddleware │
+└─────────────────────────┘ └─────────────────────────────────┘
+ │ │
+ ▼ ▼
+┌─────────────────────────┐ ┌─────────────────────────────────┐
+│ GNU Radio Objects │ │ External Services │
+│ │ │ │
+│ • Platform │ │ • Docker daemon │
+│ • FlowGraph │ │ • XML-RPC servers │
+│ • Block │ │ • Thrift servers │
+│ • Connections │ │ • Container processes │
+└─────────────────────────┘ └─────────────────────────────────┘
+```
+
+## Layer Responsibilities
+
+### MCP Layer (FastMCP)
+
+- Handles MCP protocol communication
+- Registers tools and resources
+- Manages the server lifecycle
+
+### Provider Layer
+
+Business logic that doesn't know about MCP:
+
+- **PlatformProvider**: All flowgraph operations (create, edit, validate, save)
+- **RuntimeProvider**: All runtime operations (launch, connect, control)
+
+### Middleware Layer
+
+Wraps external systems with validation and normalization:
+
+- **PlatformMiddleware**: Wraps GNU Radio's `Platform` class
+- **FlowGraphMiddleware**: Wraps `FlowGraph` with block/connection management
+- **BlockMiddleware**: Wraps `Block` with parameter/port access
+- **DockerMiddleware**: Wraps Docker SDK operations
+- **XmlRpcMiddleware**: Wraps XML-RPC client
+- **ThriftMiddleware**: Wraps ControlPort Thrift client
+- **OOTInstallerMiddleware**: Manages OOT module builds
+
+### Models Layer
+
+Pydantic models for serialization:
+
+```python
+# Examples
+class BlockModel(BaseModel):
+ name: str
+ key: str
+ state: str
+
+class ParamModel(BaseModel):
+ key: str
+ value: str
+ name: str
+ dtype: str
+
+class PortModel(BaseModel):
+ parent: str
+ key: str
+ name: str
+ dtype: str
+ direction: str
+```
+
+## Data Flow
+
+### Flowgraph Operations
+
+```
+Tool Call: make_block(block_type="osmosdr_source")
+ │
+ ▼
+McpPlatformProvider.make_block()
+ │
+ ▼
+PlatformProvider.make_block(block_name="osmosdr_source")
+ │
+ ▼
+FlowGraphMiddleware.add_block("osmosdr_source")
+ │ Creates GNU Radio Block object
+ │ Generates unique name
+ ▼
+BlockMiddleware(block)
+ │
+ ▼
+Return: "osmosdr_source_0" (str)
+```
+
+### Runtime Operations
+
+```
+Tool Call: set_variable(name="freq", value=101.1e6)
+ │
+ ▼
+McpRuntimeProvider._runtime_tools["set_variable"]
+ │
+ ▼
+RuntimeProvider.set_variable(name="freq", value=101.1e6)
+ │
+ ▼
+XmlRpcMiddleware.set_variable("freq", 101.1e6)
+ │ XML-RPC call to running flowgraph
+ ▼
+Return: True
+```
+
+## Dynamic Tool Registration
+
+Runtime tools are registered dynamically to minimize context usage:
+
+```python
+# At startup: only 5 mode control tools
+get_runtime_mode() # Check status
+enable_runtime_mode() # Register all runtime tools
+disable_runtime_mode() # Unregister runtime tools
+get_client_capabilities() # Debug client info
+list_client_roots() # Debug client roots
+
+# After enable_runtime_mode(): ~40 additional tools
+launch_flowgraph()
+connect_to_container()
+set_variable()
+# ...etc
+```
+
+
+
+## Key Design Decisions
+
+### Why Middlewares?
+
+GNU Radio objects have complex internal state and non-serializable references.
+Middlewares:
+1. Provide a clean interface with Pydantic models
+2. Handle validation and error formatting
+3. Manage resource lifecycle (connections, processes)
+
+### Why Providers?
+
+Providers separate business logic from MCP registration:
+1. Testable without MCP infrastructure
+2. Reusable in non-MCP contexts
+3. Clear dependency injection
+
+### Why Dynamic Registration?
+
+Many MCP clients include all tool descriptions in the system prompt.
+With 70+ tools, this wastes significant context. Dynamic registration:
+1. Starts with minimal tools for flowgraph design
+2. Expands when runtime control is needed
+3. Can contract back when done
+
+## File Organization
+
+```
+src/gnuradio_mcp/
+├── models.py # All Pydantic models
+├── utils.py # Helper functions
+├── oot_catalog.py # OOT module catalog
+├── middlewares/
+│ ├── platform.py # PlatformMiddleware
+│ ├── flowgraph.py # FlowGraphMiddleware
+│ ├── block.py # BlockMiddleware
+│ ├── docker.py # DockerMiddleware
+│ ├── xmlrpc.py # XmlRpcMiddleware
+│ ├── thrift.py # ThriftMiddleware
+│ └── oot.py # OOTInstallerMiddleware
+└── providers/
+ ├── base.py # PlatformProvider
+ ├── mcp.py # McpPlatformProvider
+ ├── runtime.py # RuntimeProvider
+ └── mcp_runtime.py # McpRuntimeProvider
+```
diff --git a/docs/src/content/docs/concepts/dynamic-tools.mdx b/docs/src/content/docs/concepts/dynamic-tools.mdx
new file mode 100644
index 0000000..8dd7474
--- /dev/null
+++ b/docs/src/content/docs/concepts/dynamic-tools.mdx
@@ -0,0 +1,196 @@
+---
+title: Dynamic Tools
+description: How GR-MCP dynamically registers runtime tools
+draft: false
+---
+
+import { Aside, Steps } from '@astrojs/starlight/components';
+
+GR-MCP uses **dynamic tool registration** to minimize context usage. Runtime tools
+are only registered when needed, keeping the tool list small during flowgraph design.
+
+## The Problem
+
+Many MCP clients include tool descriptions in the system prompt. With 70+ tools,
+this can consume significant context:
+
+```
+70 tools × ~100 tokens/tool = ~7,000 tokens
+```
+
+For LLM applications, this is wasteful when you only need flowgraph design tools.
+
+## The Solution
+
+GR-MCP splits tools into two groups:
+
+### Always Available (34 tools)
+
+Platform tools for flowgraph design:
+- `get_blocks`, `make_block`, `remove_block`
+- `connect_blocks`, `disconnect_blocks`
+- `validate_flowgraph`, `generate_code`
+- etc.
+
+Plus 5 runtime mode control tools:
+- `get_runtime_mode`
+- `enable_runtime_mode`
+- `disable_runtime_mode`
+- `get_client_capabilities`
+- `list_client_roots`
+
+### Dynamically Registered (~40 tools)
+
+Runtime tools loaded via `enable_runtime_mode()`:
+- Container lifecycle: `launch_flowgraph`, `stop_flowgraph`
+- XML-RPC: `connect`, `set_variable`, `get_variable`
+- ControlPort: `connect_controlport`, `get_knobs`, `get_performance_counters`
+- Coverage: `collect_coverage`, `generate_coverage_report`
+- OOT: `install_oot_module`, `detect_oot_modules`
+
+## Usage Pattern
+
+
+1. **Flowgraph Design** (29 platform tools + 5 mode tools = 34 total)
+
+ ```python
+ make_block(block_type="osmosdr_source")
+ connect_blocks(...)
+ validate_flowgraph()
+ generate_code(output_dir="/tmp")
+ ```
+
+2. **Enable Runtime** (adds ~40 tools = ~74 total)
+
+ ```python
+ enable_runtime_mode()
+ # Now runtime tools are available
+ ```
+
+3. **Runtime Control**
+
+ ```python
+ launch_flowgraph(flowgraph_path="/tmp/fm.py")
+ connect_to_container(name="gr-fm")
+ set_variable(name="freq", value=101.1e6)
+ capture_screenshot()
+ ```
+
+4. **Disable Runtime** (back to 34 tools)
+
+ ```python
+ disable_runtime_mode()
+ # Runtime tools removed, back to design mode
+ ```
+
+
+## Implementation
+
+### Mode Control Tools
+
+```python
+@self._mcp.tool
+def enable_runtime_mode() -> RuntimeModeStatus:
+ """Enable runtime mode, registering all runtime control tools."""
+ if self._runtime_enabled:
+ return RuntimeModeStatus(enabled=True, ...)
+
+ self._register_runtime_tools()
+ self._runtime_enabled = True
+ return RuntimeModeStatus(enabled=True, tools_registered=[...])
+```
+
+### Dynamic Registration
+
+```python
+def _register_runtime_tools(self):
+ """Dynamically register all runtime tools."""
+ p = self._provider
+
+ # Connection management
+ self._add_tool("connect", p.connect)
+ self._add_tool("disconnect", p.disconnect)
+ self._add_tool("get_status", p.get_status)
+
+ # Variable control
+ self._add_tool("list_variables", p.list_variables)
+ self._add_tool("get_variable", p.get_variable)
+ self._add_tool("set_variable", p.set_variable)
+
+ # ... more tools ...
+
+ # Docker-dependent tools (only if Docker available)
+ if p._has_docker:
+ self._add_tool("launch_flowgraph", p.launch_flowgraph)
+ # ...
+
+def _add_tool(self, name: str, func: Callable):
+ """Add a tool and track it for later removal."""
+ self._mcp.add_tool(func)
+ self._runtime_tools[name] = func
+```
+
+### Dynamic Unregistration
+
+```python
+def _unregister_runtime_tools(self):
+ """Remove all dynamically registered runtime tools."""
+ for name in list(self._runtime_tools.keys()):
+ self._mcp.remove_tool(name)
+ self._runtime_tools.clear()
+```
+
+## Conditional Registration
+
+Some tools depend on external services:
+
+```python
+# Docker-dependent tools
+if p._has_docker:
+ self._add_tool("launch_flowgraph", p.launch_flowgraph)
+ self._add_tool("capture_screenshot", p.capture_screenshot)
+
+ # OOT tools require Docker for building
+ if p._has_oot:
+ self._add_tool("install_oot_module", p.install_oot_module)
+```
+
+
+
+## Checking Status
+
+```python
+status = get_runtime_mode()
+# Returns: RuntimeModeStatus(
+# enabled=False,
+# tools_registered=[],
+# docker_available=True,
+# oot_available=True
+# )
+
+enable_runtime_mode()
+status = get_runtime_mode()
+# Returns: RuntimeModeStatus(
+# enabled=True,
+# tools_registered=[
+# "connect", "disconnect", "get_status",
+# "list_variables", "get_variable", "set_variable",
+# "start", "stop", "lock", "unlock",
+# "launch_flowgraph", "list_containers",
+# ...
+# ],
+# docker_available=True,
+# oot_available=True
+# )
+```
+
+## Benefits
+
+1. **Reduced Context** — ~35 tools instead of ~75 during design
+2. **Faster Responses** — Less prompt processing for simple queries
+3. **Clear Separation** — Design vs runtime is explicit
+4. **Graceful Degradation** — Missing Docker doesn't break design tools
+5. **Discovery** — `get_runtime_mode()` shows what's available
diff --git a/docs/src/content/docs/concepts/runtime-communication.mdx b/docs/src/content/docs/concepts/runtime-communication.mdx
new file mode 100644
index 0000000..6597075
--- /dev/null
+++ b/docs/src/content/docs/concepts/runtime-communication.mdx
@@ -0,0 +1,265 @@
+---
+title: Runtime Communication
+description: How GNU Radio flowgraphs communicate at runtime
+draft: false
+---
+
+import { Aside } from '@astrojs/starlight/components';
+
+This document explains how GNU Radio Companion (GRC) communicates with running flowgraph
+processes and the two mechanisms available for runtime control.
+
+## Key Insight: GRC is a Code Generator
+
+GRC runs flowgraphs as **completely separate subprocesses** via `subprocess.Popen()`.
+It does not have built-in runtime control capabilities.
+
+```
+┌────────────────────┐ subprocess.Popen() ┌─────────────────────┐
+│ GNU Radio │ ────────────────────────► │ Generated Python │
+│ Companion (GRC) │ │ Flowgraph Script │
+│ │ ◄──────────────────────── │ │
+│ (Qt/GTK GUI) │ stdout/stderr pipe │ (gr.top_block) │
+└────────────────────┘ └─────────────────────┘
+```
+
+The generated Python script runs independently. To control parameters at runtime,
+you must use one of the two communication mechanisms described below.
+
+## GRC Execution Flow
+
+```
+.grc file (YAML)
+ │
+ ▼ Platform.load_and_generate_flow_graph()
+Generator (Mako templates)
+ │
+ ▼ generator.write()
+Python script (with set_*/get_* methods)
+ │
+ ▼ ExecFlowGraphThread -> subprocess.Popen()
+Running flowgraph process
+ │
+ ▼ stdout/stderr piped back to GRC console
+```
+
+### Key GRC Execution Files
+
+| File | Purpose |
+|------|---------|
+| `grc/main.py` | Entry point |
+| `grc/gui_qt/components/executor.py` | ExecFlowGraphThread subprocess launcher |
+| `grc/core/platform.py` | Block registry, flowgraph loading |
+| `grc/core/generator/Generator.py` | Generator factory |
+| `grc/workflows/common.py` | Base generator classes |
+| `grc/workflows/python_nogui/flow_graph_nogui.py.mako` | Mako template for Python |
+
+## Two Runtime Control Mechanisms
+
+### 1. XML-RPC Server (Simple, HTTP-based)
+
+A **block-based approach** — add the `xmlrpc_server` block to your flowgraph to
+expose GRC variables over HTTP.
+
+| Aspect | Details |
+|--------|---------|
+| Protocol | HTTP (XML-RPC) |
+| Default Port | 8080 |
+| Setup | Add `XMLRPC Server` block to flowgraph |
+| Naming | `set_varname()` / `get_varname()` |
+| Type Support | Basic Python types |
+
+#### How It Works
+
+1. Add `XMLRPC Server` block to flowgraph
+2. GRC variables automatically become `set_X()` / `get_X()` methods
+3. Connect with any XML-RPC client (Python, C++, curl, etc.)
+
+#### Client Example
+
+```python
+import xmlrpc.client
+
+# Connect to running flowgraph
+server = xmlrpc.client.ServerProxy('http://localhost:8080')
+
+# Read and write variables
+print(server.get_freq()) # Read a variable
+server.set_freq(145.5e6) # Set a variable
+
+# Flowgraph control
+server.stop() # Stop flowgraph
+server.start() # Start flowgraph
+server.lock() # Lock flowgraph for modifications
+server.unlock() # Unlock flowgraph
+```
+
+#### Key Files
+
+| File | Purpose |
+|------|---------|
+| `gr-blocks/grc/xmlrpc_server.block.yml` | Server block definition |
+| `gr-blocks/grc/xmlrpc_client.block.yml` | Client block definition |
+| `gr-blocks/examples/xmlrpc/` | Example flowgraphs |
+
+### 2. ControlPort/Thrift (Advanced, Binary)
+
+A **configuration-based approach** — blocks register their parameters via `setup_rpc()`
+in C++ code.
+
+| Aspect | Details |
+|--------|---------|
+| Protocol | Thrift Binary TCP |
+| Default Port | 9090 |
+| Setup | Enable in config, blocks call `setup_rpc()` |
+| Naming | `block_alias::varname` |
+| Type Support | Rich (complex, vectors, PMT types) |
+| Metadata | Units, min/max, display hints |
+
+#### Architecture
+
+```
+┌──────────────────────────────────────────────────────────────────┐
+│ Running Flowgraph Process │
+├──────────────────────────────────────────────────────────────────┤
+│ Block A Block B │
+│ ┌──────────────────┐ ┌──────────────────┐ │
+│ │ setup_rpc() { │ │ setup_rpc() { │ │
+│ │ add_rpc_var( │ │ add_rpc_var( │ │
+│ │ "gain", │ │ "freq", │ │
+│ │ &get_gain, │ │ &get_freq, │ │
+│ │ &set_gain); │ │ &set_freq); │ │
+│ │ } │ │ } │ │
+│ └────────┬─────────┘ └────────┬─────────┘ │
+│ │ │ │
+│ ▼ ▼ │
+│ ┌──────────────────────────────────────────────────────────────┐│
+│ │ rpcserver_thrift (port 9090) ││
+│ │ ┌─────────────────┐ ┌─────────────────┐ ││
+│ │ │ setcallbackmap │ │ getcallbackmap │ ││
+│ │ │ "blockA::gain" │ │ "blockA::gain" │ ││
+│ │ │ "blockB::freq" │ │ "blockB::freq" │ ││
+│ │ └─────────────────┘ └─────────────────┘ ││
+│ └──────────────────────────────────────────────────────────────┘│
+└──────────────────────────────────────────────────────────────────┘
+ ▲
+ │ Thrift Binary Protocol (TCP)
+ ▼
+┌──────────────────────────────────────────────────────────────────┐
+│ Python Client │
+│ from gnuradio.ctrlport import GNURadioControlPortClient │
+│ │
+│ client = GNURadioControlPortClient(host='localhost', port=9090) │
+│ knobs = client.getKnobs(['blockA::gain', 'blockB::freq']) │
+│ client.setKnobs({'blockA::gain': 2.5}) │
+└──────────────────────────────────────────────────────────────────┘
+```
+
+#### Enabling ControlPort
+
+**~/.gnuradio/config.conf:**
+
+```ini
+[ControlPort]
+on = True
+edges_list = True
+
+[thrift]
+port = 9090
+nthreads = 2
+```
+
+#### Client Example
+
+```python
+from gnuradio.ctrlport.GNURadioControlPortClient import GNURadioControlPortClient
+
+# Connect to running flowgraph
+client = GNURadioControlPortClient(host='localhost', port=9090)
+
+# Get knobs (read values)
+knobs = client.getKnobs(['analog_sig_source_0::frequency'])
+print(knobs)
+
+# Set knobs (write values)
+client.setKnobs({'analog_sig_source_0::frequency': 1500.0})
+
+# Regex-based retrieval - get all frequency knobs
+all_freq_knobs = client.getRe(['.*::frequency'])
+
+# Get metadata (units, min, max, description)
+props = client.properties(['analog_sig_source_0::frequency'])
+print(props['analog_sig_source_0::frequency'].units)
+print(props['analog_sig_source_0::frequency'].min)
+```
+
+#### GUI Monitoring Tools
+
+- **gr-ctrlport-monitor** — Real-time variable inspection
+- **gr-perf-monitorx** — Performance profiling visualization
+
+```bash
+gr-ctrlport-monitor localhost 9090
+gr-perf-monitorx localhost 9090
+```
+
+#### Key Files
+
+| File | Purpose |
+|------|---------|
+| `gnuradio-runtime/lib/controlport/thrift/gnuradio.thrift` | Thrift IDL definition |
+| `gnuradio-runtime/include/gnuradio/rpcserver_thrift.h` | Server implementation |
+| `gnuradio-runtime/include/gnuradio/rpcregisterhelpers.h` | Registration templates |
+| `gnuradio-runtime/python/gnuradio/ctrlport/GNURadioControlPortClient.py` | Python client |
+| `gnuradio-runtime/python/gnuradio/ctrlport/RPCConnectionThrift.py` | Thrift connection |
+
+## Comparison: XML-RPC vs ControlPort
+
+| Feature | XML-RPC | ControlPort/Thrift |
+|---------|---------|-------------------|
+| Setup | Add block to flowgraph | Enable in config.conf |
+| Protocol | HTTP | Binary TCP |
+| Performance | Slower (text-based) | Faster (binary) |
+| Type support | Basic Python types | Complex, vectors, PMT |
+| Metadata | None | Units, min/max, hints |
+| Tooling | Any HTTP client | Specialized monitors |
+| Use case | Simple control | Performance monitoring |
+
+### When to Use Each
+
+**Use XML-RPC when:**
+- You need quick, simple remote control
+- Integration with web applications
+- Language-agnostic client access
+- Minimal configuration
+
+**Use ControlPort when:**
+- You need performance monitoring
+- Working with complex data types
+- Block-level control granularity
+- Need metadata about parameters
+
+## GR-MCP Integration
+
+GR-MCP wraps both protocols:
+
+```python
+# XML-RPC via GR-MCP
+connect_to_container(name="fm-radio")
+set_variable(name="freq", value=101.1e6)
+
+# ControlPort via GR-MCP
+connect_to_container_controlport(name="fm-profiled")
+get_performance_counters()
+```
+
+
+
+## Related Documentation
+
+- [Runtime Control Guide](/guides/runtime-control/) — XML-RPC usage in GR-MCP
+- [ControlPort Monitoring Guide](/guides/controlport/) — ControlPort usage in GR-MCP
+- GNU Radio docs: `docs/doxygen/other/ctrlport.dox` — Block implementation guide
diff --git a/docs/src/content/docs/getting-started/first-flowgraph.mdx b/docs/src/content/docs/getting-started/first-flowgraph.mdx
new file mode 100644
index 0000000..4c08016
--- /dev/null
+++ b/docs/src/content/docs/getting-started/first-flowgraph.mdx
@@ -0,0 +1,240 @@
+---
+title: Your First Flowgraph
+description: Build and validate a complete GNU Radio flowgraph with GR-MCP
+draft: false
+---
+
+import { Steps, Aside, Code, Tabs, TabItem } from '@astrojs/starlight/components';
+
+This tutorial walks through building a simple FM receiver flowgraph programmatically.
+You'll learn the core workflow: create blocks, set parameters, connect ports, validate, and save.
+
+## What We're Building
+
+A basic FM receiver chain:
+
+```
+osmosdr_source → low_pass_filter → analog_wfm_rcv → audio_sink
+```
+
+This receives RF at a configurable frequency, filters it, demodulates FM, and outputs audio.
+
+## Step-by-Step
+
+
+1. **Create the source block**
+
+ ```python
+ make_block(block_type="osmosdr_source")
+ # Returns: "osmosdr_source_0"
+ ```
+
+ GR-MCP automatically assigns unique names by appending `_0`, `_1`, etc.
+
+2. **Set source parameters**
+
+ First, inspect available parameters:
+
+ ```python
+ get_block_params(block_name="osmosdr_source_0")
+ ```
+
+ Then configure:
+
+ ```python
+ set_block_params(block_name="osmosdr_source_0", params={
+ "freq": "101.1e6", # 101.1 MHz FM station
+ "sample_rate": "2e6", # 2 MHz sample rate
+ "gain": "40", # RF gain
+ "args": '""' # Auto-detect device
+ })
+ ```
+
+3. **Create the filter**
+
+ ```python
+ make_block(block_type="low_pass_filter")
+
+ set_block_params(block_name="low_pass_filter_0", params={
+ "type": "fir_filter_ccf",
+ "decim": "10", # Decimate by 10 → 200 kHz
+ "cutoff_freq": "100e3", # 100 kHz cutoff
+ "transition_width": "10e3",
+ "win": "window.WIN_HAMMING"
+ })
+ ```
+
+4. **Create the FM demodulator**
+
+ ```python
+ make_block(block_type="analog_wfm_rcv")
+
+ set_block_params(block_name="analog_wfm_rcv_0", params={
+ "quad_rate": "200e3", # Input rate after decimation
+ "audio_decimation": "4" # Output at 50 kHz
+ })
+ ```
+
+5. **Create the audio output**
+
+ ```python
+ make_block(block_type="audio_sink")
+
+ set_block_params(block_name="audio_sink_0", params={
+ "samp_rate": "48000",
+ "device_name": '""' # Default audio device
+ })
+ ```
+
+6. **Connect the blocks**
+
+ ```python
+ connect_blocks(
+ source_block_name="osmosdr_source_0",
+ sink_block_name="low_pass_filter_0",
+ source_port_name="0",
+ sink_port_name="0"
+ )
+
+ connect_blocks(
+ source_block_name="low_pass_filter_0",
+ sink_block_name="analog_wfm_rcv_0",
+ source_port_name="0",
+ sink_port_name="0"
+ )
+
+ connect_blocks(
+ source_block_name="analog_wfm_rcv_0",
+ sink_block_name="audio_sink_0",
+ source_port_name="0",
+ sink_port_name="0"
+ )
+ ```
+
+
+
+7. **Validate the flowgraph**
+
+ ```python
+ validate_flowgraph()
+ # Returns: True
+
+ # Check for any warnings
+ get_all_errors()
+ # Returns: []
+ ```
+
+8. **Save the flowgraph**
+
+ ```python
+ save_flowgraph(filepath="/tmp/fm_receiver.grc")
+ ```
+
+ You can now open this in GNU Radio Companion!
+
+
+## Generate Python Code
+
+Instead of saving as `.grc`, you can generate executable Python directly:
+
+```python
+result = generate_code(output_dir="/tmp")
+# Returns GeneratedCodeModel with:
+# - file_path: "/tmp/fm_receiver.py"
+# - is_valid: True
+# - warnings: []
+```
+
+
+
+## Using Variables
+
+GR-MCP supports flowgraph variables for runtime tuning. Set them via flowgraph options:
+
+```python
+set_flowgraph_options(params={
+ "title": "FM Receiver",
+ "author": "Your Name",
+ # Variables are defined here too
+})
+
+# Or use the expression evaluator to test values
+evaluate_expression("101.1e6 + 200e3") # Returns: 101300000.0
+```
+
+## Complete Script
+
+Here's the full example as a Python script:
+
+```python
+#!/usr/bin/env python3
+"""Build an FM receiver with GR-MCP."""
+
+import asyncio
+from fastmcp import Client
+
+async def build_fm_receiver():
+ # Connect to GR-MCP (running as MCP server)
+ async with Client("gr-mcp") as client:
+ # Create blocks
+ await client.call_tool("make_block", {"block_type": "osmosdr_source"})
+ await client.call_tool("make_block", {"block_type": "low_pass_filter"})
+ await client.call_tool("make_block", {"block_type": "analog_wfm_rcv"})
+ await client.call_tool("make_block", {"block_type": "audio_sink"})
+
+ # Configure source
+ await client.call_tool("set_block_params", {
+ "block_name": "osmosdr_source_0",
+ "params": {
+ "freq": "101.1e6",
+ "sample_rate": "2e6",
+ "gain": "40"
+ }
+ })
+
+ # Configure filter
+ await client.call_tool("set_block_params", {
+ "block_name": "low_pass_filter_0",
+ "params": {
+ "type": "fir_filter_ccf",
+ "decim": "10",
+ "cutoff_freq": "100e3",
+ "transition_width": "10e3"
+ }
+ })
+
+ # Connect signal chain
+ for src, dst in [
+ ("osmosdr_source_0", "low_pass_filter_0"),
+ ("low_pass_filter_0", "analog_wfm_rcv_0"),
+ ("analog_wfm_rcv_0", "audio_sink_0"),
+ ]:
+ await client.call_tool("connect_blocks", {
+ "source_block_name": src,
+ "sink_block_name": dst,
+ "source_port_name": "0",
+ "sink_port_name": "0"
+ })
+
+ # Validate and save
+ result = await client.call_tool("validate_flowgraph", {})
+ print(f"Valid: {result.data}")
+
+ await client.call_tool("save_flowgraph", {
+ "filepath": "/tmp/fm_receiver.grc"
+ })
+
+if __name__ == "__main__":
+ asyncio.run(build_fm_receiver())
+```
+
+## Next Steps
+
+- [Running in Docker](/getting-started/running-in-docker/) — Launch the flowgraph with runtime control
+- [OOT Modules](/guides/oot-modules/) — Add gr-osmosdr and other modules
diff --git a/docs/src/content/docs/getting-started/installation.mdx b/docs/src/content/docs/getting-started/installation.mdx
new file mode 100644
index 0000000..edb0ea4
--- /dev/null
+++ b/docs/src/content/docs/getting-started/installation.mdx
@@ -0,0 +1,173 @@
+---
+title: Installation
+description: Set up GR-MCP with UV and GNU Radio
+draft: false
+---
+
+import { Steps, Tabs, TabItem, Aside } from '@astrojs/starlight/components';
+
+GR-MCP requires Python 3.14+, GNU Radio with GRC, and optionally Docker for runtime control features.
+
+## Prerequisites
+
+
+
+```bash
+# GNU Radio and GRC
+sudo pacman -S gnuradio gnuradio-companion
+
+# UV package manager (if not installed)
+curl -LsSf https://astral.sh/uv/install.sh | sh
+
+# Docker (optional, for runtime control)
+sudo pacman -S docker docker-compose
+sudo systemctl enable --now docker
+sudo usermod -aG docker $USER
+```
+
+
+```bash
+# GNU Radio and GRC
+sudo apt install gnuradio gnuradio-dev
+
+# UV package manager
+curl -LsSf https://astral.sh/uv/install.sh | sh
+
+# Docker (optional)
+sudo apt install docker.io docker-compose
+sudo systemctl enable --now docker
+sudo usermod -aG docker $USER
+```
+
+
+```bash
+# GNU Radio and GRC
+sudo dnf install gnuradio gnuradio-devel
+
+# UV package manager
+curl -LsSf https://astral.sh/uv/install.sh | sh
+
+# Docker (optional)
+sudo dnf install docker docker-compose
+sudo systemctl enable --now docker
+sudo usermod -aG docker $USER
+```
+
+
+
+## Install GR-MCP
+
+
+1. **Clone the repository**
+
+ ```bash
+ git clone https://git.supported.systems/MCP/gr-mcp
+ cd gr-mcp
+ ```
+
+2. **Create a virtual environment with system site-packages**
+
+
+
+ ```bash
+ uv venv --system-site-packages --python 3.14
+ ```
+
+3. **Install dependencies**
+
+ ```bash
+ uv sync
+ ```
+
+4. **Verify the installation**
+
+ ```bash
+ uv run main.py
+ ```
+
+ You should see the FastMCP server start. Press `Ctrl+C` to stop.
+
+
+## Configure Your MCP Client
+
+Add GR-MCP to your MCP client configuration:
+
+
+
+Edit `~/.config/claude/claude_desktop_config.json`:
+
+```json
+{
+ "mcpServers": {
+ "gr-mcp": {
+ "command": "uv",
+ "args": ["--directory", "/path/to/gr-mcp", "run", "main.py"]
+ }
+ }
+}
+```
+
+
+```bash
+claude mcp add gr-mcp -- uv --directory /path/to/gr-mcp run main.py
+```
+
+Or add to your project's `.mcp.json`:
+
+```json
+{
+ "mcpServers": {
+ "gr-mcp": {
+ "command": "uv",
+ "args": ["--directory", "/path/to/gr-mcp", "run", "main.py"]
+ }
+ }
+}
+```
+
+
+Edit your Cursor settings or `.cursor/mcp.json`:
+
+```json
+{
+ "mcpServers": {
+ "gr-mcp": {
+ "command": "uv",
+ "args": ["--directory", "/path/to/gr-mcp", "run", "main.py"]
+ }
+ }
+}
+```
+
+
+
+## Build Docker Images (Optional)
+
+For runtime control features (launching flowgraphs in containers, visual feedback, code coverage):
+
+```bash
+cd gr-mcp
+
+# Base runtime image with Xvfb + VNC + ImageMagick
+docker build -f docker/Dockerfile.gnuradio-runtime \
+ -t gnuradio-runtime:latest docker/
+
+# Coverage image (adds python3-coverage)
+docker build -f docker/Dockerfile.gnuradio-coverage \
+ -t gnuradio-coverage:latest docker/
+```
+
+
+
+## Next Steps
+
+Now that GR-MCP is installed, try:
+
+- [Build your first flowgraph](/getting-started/first-flowgraph/) — Create and validate a complete signal chain
+- [Running in Docker](/getting-started/running-in-docker/) — Launch flowgraphs with runtime control
diff --git a/docs/src/content/docs/getting-started/running-in-docker.mdx b/docs/src/content/docs/getting-started/running-in-docker.mdx
new file mode 100644
index 0000000..a67696a
--- /dev/null
+++ b/docs/src/content/docs/getting-started/running-in-docker.mdx
@@ -0,0 +1,270 @@
+---
+title: Running in Docker
+description: Launch flowgraphs in Docker containers with runtime control
+draft: false
+---
+
+import { Steps, Aside, Tabs, TabItem } from '@astrojs/starlight/components';
+
+GR-MCP can run flowgraphs in Docker containers, providing isolation, headless GUI rendering
+(via Xvfb), and real-time control through XML-RPC or ControlPort.
+
+## Prerequisites
+
+- Docker daemon running (`sudo systemctl start docker`)
+- User in the `docker` group (`sudo usermod -aG docker $USER`)
+- Base runtime image built (see [Installation](/getting-started/installation/))
+
+## Enable Runtime Mode
+
+Runtime tools are not loaded by default to minimize context usage. Enable them first:
+
+```python
+enable_runtime_mode()
+# Returns RuntimeModeStatus with:
+# enabled: True
+# tools_registered: ["launch_flowgraph", "connect_to_container", ...]
+# docker_available: True
+# oot_available: True
+```
+
+
+
+## Launch a Flowgraph
+
+
+1. **Generate the Python script**
+
+ Docker containers run `.py` files, not `.grc` files. Generate code first:
+
+ ```python
+ generate_code(output_dir="/tmp")
+ # Creates /tmp/fm_receiver.py
+ ```
+
+2. **Launch in a container**
+
+ ```python
+ launch_flowgraph(
+ flowgraph_path="/tmp/fm_receiver.py",
+ name="fm-radio",
+ xmlrpc_port=8080, # Enable XML-RPC on this port
+ enable_vnc=True # Optional: VNC server on port 5900
+ )
+ ```
+
+ Returns a `ContainerModel` with:
+ - `name`: Container name
+ - `status`: "running"
+ - `xmlrpc_port`: Mapped port number
+ - `vnc_port`: VNC port (if enabled)
+
+3. **Connect to the running flowgraph**
+
+ ```python
+ connect_to_container(name="fm-radio")
+ ```
+
+ This auto-discovers the XML-RPC port from container labels.
+
+
+## Real-Time Variable Control
+
+Once connected, you can read and modify flowgraph variables:
+
+```python
+# List available variables
+list_variables()
+# Returns: [VariableModel(name="freq", value=101100000.0), ...]
+
+# Read a variable
+get_variable(name="freq")
+# Returns: 101100000.0
+
+# Tune to a different station
+set_variable(name="freq", value=98.5e6)
+```
+
+### Thread-Safe Updates
+
+For multiple parameter changes, lock the flowgraph first:
+
+```python
+lock() # Pause processing
+
+set_variable(name="freq", value=102.7e6)
+set_variable(name="gain", value=35)
+
+unlock() # Resume processing
+```
+
+## Visual Feedback
+
+### Screenshots
+
+Capture the QT GUI display:
+
+```python
+screenshot = capture_screenshot(name="fm-radio")
+# Returns ScreenshotModel with:
+# path: "/tmp/gr-mcp-screenshots/fm-radio-2024-01-15-14-30-00.png"
+# width: 1024
+# height: 768
+```
+
+### VNC Access
+
+If launched with `enable_vnc=True`, connect with any VNC client:
+
+```bash
+# Using TigerVNC
+vncviewer localhost:5900
+
+# Using Remmina
+remmina -c vnc://localhost:5900
+```
+
+### Container Logs
+
+Check for errors or debug output:
+
+```python
+get_container_logs(name="fm-radio", tail=50)
+# Returns last 50 lines of stdout/stderr
+```
+
+## Container Lifecycle
+
+```python
+# List all GR-MCP containers
+list_containers()
+
+# Stop a running flowgraph (graceful shutdown)
+stop_flowgraph(name="fm-radio")
+
+# Remove the container
+remove_flowgraph(name="fm-radio")
+
+# Force remove if stuck
+remove_flowgraph(name="fm-radio", force=True)
+```
+
+## Flowgraph Execution Control
+
+Control the flowgraph's execution state via XML-RPC:
+
+```python
+stop() # Pause execution (flowgraph.stop())
+start() # Resume execution (flowgraph.start())
+```
+
+## Using SDR Hardware
+
+Pass device paths to access hardware inside the container:
+
+```python
+launch_flowgraph(
+ flowgraph_path="/tmp/fm_receiver.py",
+ name="fm-hardware",
+ device_paths=["/dev/bus/usb"] # RTL-SDR access
+)
+```
+
+
+
+```python
+device_paths=["/dev/bus/usb"]
+```
+
+
+```python
+device_paths=["/dev/bus/usb"]
+```
+
+
+```python
+device_paths=["/dev/bus/usb"]
+```
+
+
+```python
+device_paths=["/dev/ttyUSB0"]
+```
+
+
+
+## Auto-Image Selection
+
+For flowgraphs using OOT modules, let GR-MCP auto-detect and build the required image:
+
+```python
+launch_flowgraph(
+ flowgraph_path="lora_rx.py",
+ auto_image=True # Detects gr-lora_sdr, builds/uses appropriate image
+)
+```
+
+This:
+1. Analyzes the flowgraph for OOT imports
+2. Checks if required modules are installed
+3. Builds missing modules from the OOT catalog
+4. Creates a combo image if multiple OOT modules are needed
+
+## Complete Example
+
+```python
+#!/usr/bin/env python3
+"""Launch and control an FM receiver via Docker."""
+
+import asyncio
+import time
+from fastmcp import Client
+
+async def main():
+ async with Client("gr-mcp") as client:
+ # Enable runtime tools
+ await client.call_tool("enable_runtime_mode", {})
+
+ # Launch container
+ result = await client.call_tool("launch_flowgraph", {
+ "flowgraph_path": "/tmp/fm_receiver.py",
+ "name": "fm-demo",
+ "xmlrpc_port": 8080,
+ "enable_vnc": True
+ })
+ print(f"Container: {result.data.name}")
+
+ # Wait for startup
+ time.sleep(3)
+
+ # Connect
+ await client.call_tool("connect_to_container", {"name": "fm-demo"})
+
+ # Scan FM band
+ for freq in [88.1e6, 95.5e6, 101.1e6, 107.9e6]:
+ await client.call_tool("set_variable", {
+ "name": "freq",
+ "value": freq
+ })
+ print(f"Tuned to {freq/1e6:.1f} MHz")
+ time.sleep(2)
+
+ # Capture final state
+ await client.call_tool("capture_screenshot", {"name": "fm-demo"})
+
+ # Cleanup
+ await client.call_tool("stop_flowgraph", {"name": "fm-demo"})
+ await client.call_tool("remove_flowgraph", {"name": "fm-demo"})
+
+if __name__ == "__main__":
+ asyncio.run(main())
+```
+
+## Next Steps
+
+- [Runtime Control Guide](/guides/runtime-control/) — Advanced XML-RPC patterns
+- [ControlPort Monitoring](/guides/controlport/) — Performance metrics via Thrift
+- [Code Coverage](/guides/code-coverage/) — Collect coverage from containerized runs
diff --git a/docs/src/content/docs/guides/code-coverage.mdx b/docs/src/content/docs/guides/code-coverage.mdx
new file mode 100644
index 0000000..fb3ad85
--- /dev/null
+++ b/docs/src/content/docs/guides/code-coverage.mdx
@@ -0,0 +1,287 @@
+---
+title: Code Coverage
+description: Collect Python code coverage from containerized flowgraphs
+draft: false
+---
+
+import { Steps, Aside, Tabs, TabItem } from '@astrojs/starlight/components';
+
+GR-MCP can collect Python code coverage from flowgraphs running in Docker containers.
+This is useful for measuring test coverage of GNU Radio applications and embedded Python blocks.
+
+## How It Works
+
+1. Launch with `enable_coverage=True` — container runs flowgraph under `coverage.py`
+2. Run your test scenarios via XML-RPC variable control
+3. Stop the flowgraph gracefully — coverage data is written to disk
+4. Collect and analyze — generate reports in HTML, XML, or JSON
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ Docker Container │
+│ ┌─────────────────────────────────────────────────────────┐│
+│ │ coverage run flowgraph.py ││
+│ │ └─ writes .coverage.* files on exit ││
+│ └─────────────────────────────────────────────────────────┘│
+│ │ │
+│ ▼ │
+│ /tmp/gr-mcp-coverage/{container_name}/ │
+│ ├─ .coverage │
+│ ├─ .coverage.{hostname}.{pid}.* │
+│ └─ htmlcov/ │
+└─────────────────────────────────────────────────────────────┘
+```
+
+## Prerequisites
+
+Build the coverage-enabled Docker image:
+
+```bash
+docker build -f docker/Dockerfile.gnuradio-coverage \
+ -t gnuradio-coverage:latest docker/
+```
+
+
+
+## Collect Coverage
+
+
+1. **Launch with coverage enabled**
+
+ ```python
+ enable_runtime_mode()
+
+ launch_flowgraph(
+ flowgraph_path="/tmp/flowgraph.py",
+ name="coverage-test",
+ enable_coverage=True
+ )
+ ```
+
+2. **Run your test scenario**
+
+ ```python
+ connect_to_container(name="coverage-test")
+
+ # Exercise the flowgraph
+ set_variable(name="freq", value=100e6)
+ time.sleep(2)
+ set_variable(name="freq", value=200e6)
+ time.sleep(2)
+ # ... more test actions ...
+ ```
+
+3. **Stop gracefully** (required for coverage data)
+
+ ```python
+ stop_flowgraph(name="coverage-test")
+ ```
+
+
+
+4. **Collect the coverage data**
+
+ ```python
+ collect_coverage(name="coverage-test")
+ # Returns CoverageDataModel:
+ # container_name: "coverage-test"
+ # coverage_file: "/tmp/gr-mcp-coverage/coverage-test/.coverage"
+ # summary: "Name Stmts Miss Cover\n..."
+ # lines_covered: 150
+ # lines_total: 200
+ # coverage_percent: 75.0
+ ```
+
+5. **Generate a report**
+
+ ```python
+ generate_coverage_report(
+ name="coverage-test",
+ format="html"
+ )
+ # Returns CoverageReportModel:
+ # report_path: "/tmp/gr-mcp-coverage/coverage-test/htmlcov/index.html"
+ ```
+
+
+## Report Formats
+
+
+
+```python
+generate_coverage_report(name="coverage-test", format="html")
+# Creates: /tmp/gr-mcp-coverage/coverage-test/htmlcov/index.html
+```
+
+Best for visual inspection — shows line-by-line coverage with highlighting.
+
+
+```python
+generate_coverage_report(name="coverage-test", format="xml")
+# Creates: /tmp/gr-mcp-coverage/coverage-test/coverage.xml
+```
+
+Cobertura-compatible format for CI/CD integration.
+
+
+```python
+generate_coverage_report(name="coverage-test", format="json")
+# Creates: /tmp/gr-mcp-coverage/coverage-test/coverage.json
+```
+
+Machine-readable format for custom analysis.
+
+
+
+## Combine Multiple Runs
+
+Aggregate coverage from multiple test scenarios:
+
+```python
+# Run first scenario
+launch_flowgraph(..., name="test-1", enable_coverage=True)
+# ... exercise ...
+stop_flowgraph(name="test-1")
+
+# Run second scenario
+launch_flowgraph(..., name="test-2", enable_coverage=True)
+# ... exercise ...
+stop_flowgraph(name="test-2")
+
+# Combine results
+combined = combine_coverage(names=["test-1", "test-2"])
+# Returns CoverageDataModel for combined data
+
+# Generate combined report
+generate_coverage_report(name="combined", format="html")
+```
+
+## Clean Up Coverage Data
+
+```python
+# Delete specific container's coverage
+delete_coverage(name="coverage-test")
+# Returns: 1 (directories deleted)
+
+# Delete old coverage data
+delete_coverage(older_than_days=7)
+# Returns: number of directories deleted
+
+# Delete all coverage data
+delete_coverage()
+# Returns: number of directories deleted
+```
+
+## Example: Test Suite with Coverage
+
+```python
+#!/usr/bin/env python3
+"""Run a test suite with coverage collection."""
+
+import asyncio
+import time
+from fastmcp import Client
+
+TEST_SCENARIOS = [
+ {"name": "low-freq", "freq": 50e6, "duration": 5},
+ {"name": "mid-freq", "freq": 500e6, "duration": 5},
+ {"name": "high-freq", "freq": 2.4e9, "duration": 5},
+]
+
+async def run_test_suite():
+ async with Client("gr-mcp") as client:
+ await client.call_tool("enable_runtime_mode", {})
+
+ container_names = []
+
+ for scenario in TEST_SCENARIOS:
+ name = f"cov-{scenario['name']}"
+ container_names.append(name)
+
+ print(f"Running scenario: {scenario['name']}")
+
+ # Launch with coverage
+ await client.call_tool("launch_flowgraph", {
+ "flowgraph_path": "/tmp/radio_app.py",
+ "name": name,
+ "enable_coverage": True
+ })
+
+ time.sleep(3)
+
+ # Connect and run scenario
+ await client.call_tool("connect_to_container", {"name": name})
+ await client.call_tool("set_variable", {
+ "name": "freq",
+ "value": scenario["freq"]
+ })
+
+ time.sleep(scenario["duration"])
+
+ # Stop gracefully for coverage
+ await client.call_tool("stop_flowgraph", {"name": name})
+
+ # Collect this run's coverage
+ result = await client.call_tool("collect_coverage", {"name": name})
+ print(f" Coverage: {result.data.coverage_percent}%")
+
+ # Combine all runs
+ print("\nCombining coverage from all scenarios...")
+ combined = await client.call_tool("combine_coverage", {
+ "names": container_names
+ })
+ print(f"Combined coverage: {combined.data.coverage_percent}%")
+
+ # Generate final report
+ await client.call_tool("generate_coverage_report", {
+ "name": "combined",
+ "format": "html"
+ })
+ print("HTML report: /tmp/gr-mcp-coverage/combined/htmlcov/index.html")
+
+ # Cleanup containers
+ for name in container_names:
+ await client.call_tool("remove_flowgraph", {"name": name})
+
+if __name__ == "__main__":
+ asyncio.run(run_test_suite())
+```
+
+## Coverage for Embedded Python Blocks
+
+Coverage includes any embedded Python blocks in your flowgraph:
+
+```python
+# Create an embedded block
+source = """
+import numpy as np
+from gnuradio import gr
+
+class my_block(gr.sync_block):
+ def __init__(self):
+ gr.sync_block.__init__(self, ...)
+
+ def work(self, input_items, output_items):
+ # This code path will show in coverage
+ if input_items[0][0] > 0.5:
+ output_items[0][:] = input_items[0] * 2
+ else:
+ output_items[0][:] = input_items[0]
+ return len(output_items[0])
+"""
+
+create_embedded_python_block(source_code=source)
+```
+
+Coverage reports will show which branches of your embedded block were exercised.
+
+## Next Steps
+
+- [Running in Docker](/getting-started/running-in-docker/) — Container launch basics
+- [Runtime Control](/guides/runtime-control/) — Control flowgraphs during tests
diff --git a/docs/src/content/docs/guides/controlport.mdx b/docs/src/content/docs/guides/controlport.mdx
new file mode 100644
index 0000000..cb8ac06
--- /dev/null
+++ b/docs/src/content/docs/guides/controlport.mdx
@@ -0,0 +1,262 @@
+---
+title: ControlPort Monitoring
+description: Advanced runtime control with performance counters and Thrift
+draft: false
+---
+
+import { Steps, Aside, Tabs, TabItem } from '@astrojs/starlight/components';
+
+ControlPort is GNU Radio's binary protocol for advanced runtime control. It provides
+richer functionality than XML-RPC: native type support, performance counters, knob
+metadata, and PMT message injection.
+
+## ControlPort vs XML-RPC
+
+| Feature | XML-RPC | ControlPort |
+|---------|---------|-------------|
+| Protocol | HTTP (text) | Thrift (binary) |
+| Performance | Slower | Faster |
+| Type Support | Basic Python | Complex, vectors, PMT |
+| Metadata | None | Units, min/max, hints |
+| Perf Counters | No | Yes |
+| Message Ports | No | Yes |
+
+**Use ControlPort when:**
+- You need performance profiling (throughput, timing, buffer utilization)
+- Working with complex data types (complex numbers, vectors)
+- You want parameter metadata (units, valid ranges)
+- You need to send PMT messages to blocks
+
+## Enable ControlPort
+
+Launch with ControlPort enabled:
+
+```python
+enable_runtime_mode()
+
+launch_flowgraph(
+ flowgraph_path="/tmp/fm_receiver.py",
+ name="fm-profiled",
+ enable_controlport=True, # Enable ControlPort
+ controlport_port=9090, # Port (default 9090)
+ enable_perf_counters=True # Enable performance counters
+)
+```
+
+
+
+## Connect via ControlPort
+
+
+
+```python
+connect_to_container_controlport(name="fm-profiled")
+```
+
+
+```python
+connect_controlport(host="127.0.0.1", port=9090)
+```
+
+
+
+## Working with Knobs
+
+ControlPort exposes block parameters as "knobs" with the naming pattern:
+`block_alias::parameter`
+
+### Get Knobs
+
+```python
+# Get all knobs
+get_knobs(pattern="")
+# Returns: [KnobModel(name="osmosdr_source_0::freq_corr", value=0.0, ...), ...]
+
+# Filter by regex
+get_knobs(pattern=".*::freq.*")
+# Returns: [KnobModel(name="osmosdr_source_0::freq_corr", ...), ...]
+
+# Get knobs for a specific block
+get_knobs(pattern="osmosdr_source_0::.*")
+```
+
+### Set Knobs
+
+```python
+# Set multiple knobs atomically
+set_knobs({
+ "sig_source_0::frequency": 1500.0,
+ "sig_source_0::amplitude": 0.8
+})
+```
+
+### Get Knob Properties
+
+```python
+get_knob_properties(names=["sig_source_0::frequency"])
+# Returns: [KnobPropertiesModel(
+# name="sig_source_0::frequency",
+# units="Hz",
+# min=0.0,
+# max=1e9,
+# description="Output signal frequency"
+# )]
+```
+
+## Performance Counters
+
+Monitor block performance in real-time:
+
+```python
+get_performance_counters()
+# Returns: [PerfCounterModel(
+# block="low_pass_filter_0",
+# nproduced=1048576,
+# nconsumed=10485760,
+# avg_work_time_us=45.3,
+# avg_throughput=23.2e6,
+# pc_input_buffers_full=0.85,
+# pc_output_buffers_full=0.12
+# ), ...]
+
+# Filter by block
+get_performance_counters(block="low_pass_filter_0")
+```
+
+### Performance Metrics Explained
+
+| Metric | Description |
+|--------|-------------|
+| `nproduced` | Items produced by block |
+| `nconsumed` | Items consumed by block |
+| `avg_work_time_us` | Average time in `work()` function |
+| `avg_throughput` | Items per second |
+| `pc_input_buffers_full` | Input buffer utilization (0-1) |
+| `pc_output_buffers_full` | Output buffer utilization (0-1) |
+
+
+
+## PMT Message Injection
+
+Send messages to block message ports:
+
+```python
+# Send a simple string message
+post_message(
+ block="pdu_sink_0",
+ port="pdus",
+ message="hello"
+)
+
+# Send a dict (converted to PMT dict)
+post_message(
+ block="command_handler_0",
+ port="command",
+ message={"freq": 1e6, "gain": 40}
+)
+```
+
+## Example: Performance Monitor
+
+```python
+#!/usr/bin/env python3
+"""Monitor flowgraph performance and identify bottlenecks."""
+
+import asyncio
+import time
+from fastmcp import Client
+
+async def monitor_performance():
+ async with Client("gr-mcp") as client:
+ await client.call_tool("enable_runtime_mode", {})
+
+ # Launch with ControlPort
+ await client.call_tool("launch_flowgraph", {
+ "flowgraph_path": "/tmp/signal_chain.py",
+ "name": "perf-test",
+ "enable_controlport": True,
+ "enable_perf_counters": True
+ })
+
+ time.sleep(5) # Let it warm up
+
+ # Connect via ControlPort
+ await client.call_tool("connect_to_container_controlport", {
+ "name": "perf-test"
+ })
+
+ # Sample performance 10 times
+ for i in range(10):
+ result = await client.call_tool("get_performance_counters", {})
+
+ print(f"\n--- Sample {i+1} ---")
+ for counter in result.data:
+ buffer_status = "OK"
+ if counter.pc_input_buffers_full > 0.9:
+ buffer_status = "INPUT BOTTLENECK"
+ elif counter.pc_output_buffers_full > 0.9:
+ buffer_status = "BLOCK BOTTLENECK"
+
+ print(f"{counter.block}: {counter.avg_throughput/1e6:.2f} MSps, "
+ f"work={counter.avg_work_time_us:.1f}us [{buffer_status}]")
+
+ time.sleep(1)
+
+ # Cleanup
+ await client.call_tool("disconnect_controlport", {})
+ await client.call_tool("stop_flowgraph", {"name": "perf-test"})
+ await client.call_tool("remove_flowgraph", {"name": "perf-test"})
+
+if __name__ == "__main__":
+ asyncio.run(monitor_performance())
+```
+
+## Using Both XML-RPC and ControlPort
+
+You can have both connections active simultaneously:
+
+```python
+# Launch with both
+launch_flowgraph(
+ flowgraph_path="...",
+ name="dual-control",
+ xmlrpc_port=8080,
+ enable_controlport=True
+)
+
+# Connect to XML-RPC for variable control
+connect_to_container(name="dual-control")
+
+# Connect to ControlPort for performance monitoring
+connect_to_container_controlport(name="dual-control")
+
+# Use XML-RPC for simple variable changes
+set_variable(name="freq", value=101.1e6)
+
+# Use ControlPort for performance data
+get_performance_counters()
+
+# Disconnect both
+disconnect() # Disconnects both XML-RPC and ControlPort
+```
+
+## Disconnect ControlPort
+
+```python
+# Disconnect only ControlPort (keep XML-RPC)
+disconnect_controlport()
+
+# Or disconnect everything
+disconnect()
+```
+
+## Next Steps
+
+- [Runtime Communication Concepts](/concepts/runtime-communication/) — Deep dive into protocol details
+- [Code Coverage](/guides/code-coverage/) — Collect test coverage from runtime
diff --git a/docs/src/content/docs/guides/oot-modules.mdx b/docs/src/content/docs/guides/oot-modules.mdx
new file mode 100644
index 0000000..5edd1ac
--- /dev/null
+++ b/docs/src/content/docs/guides/oot-modules.mdx
@@ -0,0 +1,251 @@
+---
+title: Working with OOT Modules
+description: Install and manage GNU Radio Out-of-Tree modules
+draft: false
+---
+
+import { Steps, Aside, Tabs, TabItem, CardGrid, Card } from '@astrojs/starlight/components';
+
+Out-of-Tree (OOT) modules extend GNU Radio with specialized functionality like LoRa demodulation,
+ADS-B decoding, or hardware support. GR-MCP provides tools to discover, install, and combine
+OOT modules into Docker images.
+
+## Browse Available Modules
+
+GR-MCP includes a curated catalog of 24 OOT modules accessible via MCP resources:
+
+```python
+# List all modules
+# Resource: oot://directory
+
+# Get details for a specific module
+# Resource: oot://directory/lora_sdr
+```
+
+
+
+ Available in the base `gnuradio-runtime` image: osmosdr, satellites, gsm, rds, fosphor, air_modes, etc.
+
+
+ Built on-demand: lora_sdr, ieee802_11, adsb, iridium, dab, nrsc5, etc.
+
+
+
+### Module Categories
+
+| Category | Modules |
+|----------|---------|
+| **Hardware** | osmosdr, funcube, hpsdr, limesdr |
+| **Satellite** | satellites, satnogs, iridium, leo |
+| **Cellular** | gsm |
+| **Broadcast** | rds, dab, dl5eu, nrsc5 |
+| **Aviation** | air_modes, adsb |
+| **IoT** | lora_sdr, ieee802_15_4 |
+| **WiFi** | ieee802_11 |
+| **Analysis** | iqbal, radar, inspector |
+| **Visualization** | fosphor |
+| **Utility** | foo |
+
+## Install a Module
+
+
+1. **Enable runtime mode** (required for Docker operations)
+
+ ```python
+ enable_runtime_mode()
+ ```
+
+2. **Install the module**
+
+ For catalog modules:
+
+ ```python
+ install_oot_module(
+ git_url="https://github.com/tapparelj/gr-lora_sdr",
+ branch="master"
+ )
+ ```
+
+ This:
+ - Clones the repository
+ - Compiles with cmake
+ - Creates a Docker image: `gnuradio-lora_sdr-runtime:latest`
+
+3. **Use the new image**
+
+ ```python
+ launch_flowgraph(
+ flowgraph_path="lora_rx.py",
+ image="gnuradio-lora_sdr-runtime:latest"
+ )
+ ```
+
+
+### With Build Dependencies
+
+Some modules need extra packages for compilation:
+
+```python
+install_oot_module(
+ git_url="https://github.com/hboeglen/gr-dab",
+ branch="maint-3.10",
+ build_deps=["autoconf", "automake", "libtool", "libfaad-dev"],
+ cmake_args=["-DENABLE_DOXYGEN=OFF"]
+)
+```
+
+
+
+## Detect Required Modules
+
+GR-MCP can analyze flowgraphs to identify OOT dependencies:
+
+```python
+detect_oot_modules(flowgraph_path="lora_rx.py")
+# Returns OOTDetectionResult:
+# detected_modules: ["lora_sdr"]
+# unknown_blocks: []
+# recommended_image: "gnuradio-lora_sdr-runtime:latest"
+```
+
+For `.grc` files, detection uses prefix matching against the catalog.
+For `.py` files, it parses Python imports for more accuracy.
+
+## Auto-Image Selection
+
+The simplest approach: let GR-MCP handle everything:
+
+```python
+launch_flowgraph(
+ flowgraph_path="lora_rx.py",
+ auto_image=True
+)
+```
+
+This automatically:
+1. Detects required OOT modules
+2. Installs missing modules from the catalog
+3. Builds combo images if multiple modules are needed
+4. Launches with the appropriate image
+
+## Combine Multiple Modules
+
+Some flowgraphs need multiple OOT modules. Create a combo image:
+
+```python
+build_multi_oot_image(module_names=["lora_sdr", "adsb"])
+# Returns ComboImageResult:
+# success: True
+# image: ComboImageInfo(
+# combo_key: "combo:adsb+lora_sdr",
+# image_tag: "gr-combo-adsb-lora_sdr:latest",
+# modules: ["adsb", "lora_sdr"]
+# )
+```
+
+Missing modules are auto-built from the catalog.
+
+### Launch with Combo Image
+
+```python
+launch_flowgraph(
+ flowgraph_path="multi_protocol_rx.py",
+ image="gr-combo-adsb-lora_sdr:latest"
+)
+```
+
+Or use auto-detection:
+
+```python
+detect_oot_modules("multi_protocol_rx.py")
+# detected_modules: ["lora_sdr", "adsb"]
+# recommended_image: "gr-combo-adsb-lora_sdr:latest"
+```
+
+## Manage Installed Images
+
+```python
+# List installed OOT images
+list_oot_images()
+# Returns: [OOTImageInfo(module_name="lora_sdr", image_tag="..."), ...]
+
+# List combo images
+list_combo_images()
+# Returns: [ComboImageInfo(combo_key="combo:adsb+lora_sdr", ...)]
+
+# Remove a single-module image
+remove_oot_image(module_name="lora_sdr")
+
+# Remove a combo image
+remove_combo_image(combo_key="combo:adsb+lora_sdr")
+```
+
+## Load OOT Blocks at Design Time
+
+For flowgraph design (without Docker), load OOT block definitions:
+
+```python
+# Add a path containing .block.yml files
+add_block_path("/usr/local/share/gnuradio/grc/blocks")
+
+# Check current paths and block count
+get_block_paths()
+# Returns BlockPathsModel with paths and total_blocks
+
+# Load multiple paths at once
+load_oot_blocks(paths=[
+ "/usr/local/share/gnuradio/grc/blocks",
+ "/home/user/gr-modules/lib/grc"
+])
+```
+
+
+
+## Example: LoRa Receiver Setup
+
+Complete workflow for receiving LoRa packets:
+
+```python
+# 1. Enable runtime mode
+enable_runtime_mode()
+
+# 2. Check if lora_sdr is available
+# Resource: oot://directory/lora_sdr
+# -> preinstalled: False, installed: False
+
+# 3. Install the module
+install_oot_module(
+ git_url="https://github.com/tapparelj/gr-lora_sdr",
+ branch="master"
+)
+# -> image_tag: "gnuradio-lora_sdr-runtime:latest"
+
+# 4. Load blocks for design
+add_block_path("/usr/local/share/gnuradio/grc/blocks")
+
+# 5. Build the flowgraph
+make_block(block_type="lora_sdr_lora_sdr_lora_rx")
+# ... configure and connect ...
+
+# 6. Generate code
+generate_code(output_dir="/tmp")
+
+# 7. Launch with the new image
+launch_flowgraph(
+ flowgraph_path="/tmp/lora_rx.py",
+ name="lora-receiver",
+ image="gnuradio-lora_sdr-runtime:latest",
+ device_paths=["/dev/bus/usb"]
+)
+```
+
+## Next Steps
+
+- [Runtime Control](/guides/runtime-control/) — Control variables while the flowgraph runs
+- [OOT Catalog Reference](/reference/oot-catalog/) — Full module list with install examples
diff --git a/docs/src/content/docs/guides/runtime-control.mdx b/docs/src/content/docs/guides/runtime-control.mdx
new file mode 100644
index 0000000..de4e4c8
--- /dev/null
+++ b/docs/src/content/docs/guides/runtime-control.mdx
@@ -0,0 +1,207 @@
+---
+title: Runtime Control
+description: Control flowgraph variables and execution in real-time
+draft: false
+---
+
+import { Steps, Aside, Tabs, TabItem } from '@astrojs/starlight/components';
+
+Once a flowgraph is running, you can read and modify variables, control execution,
+and monitor status through XML-RPC. This enables real-time tuning without restarting.
+
+## How It Works
+
+Generated flowgraphs expose variables through an XML-RPC server:
+
+```
+┌─────────────────────────────┐ XML-RPC (HTTP) ┌─────────────────┐
+│ GR-MCP │ ←─────────────────────→ │ Flowgraph │
+│ (MCP Server) │ get_freq() │ (Python) │
+│ │ set_freq(101.1e6) │ │
+└─────────────────────────────┘ └─────────────────┘
+```
+
+The flowgraph's variables become `get_X()` and `set_X()` methods automatically.
+
+## Prerequisites
+
+Your flowgraph must include an XML-RPC server block, or be launched via GR-MCP's
+`launch_flowgraph()` which configures this automatically.
+
+
+
+## Connect to a Flowgraph
+
+
+
+```python
+# Most common: connect to a GR-MCP container
+connect_to_container(name="fm-radio")
+```
+
+
+```python
+# Direct connection to any XML-RPC endpoint
+connect(url="http://localhost:8080")
+```
+
+
+
+Both methods return `ConnectionInfoModel` with endpoint details.
+
+## Variable Operations
+
+### List Variables
+
+```python
+list_variables()
+# Returns: [
+# VariableModel(name="freq", value=101100000.0, type="float"),
+# VariableModel(name="gain", value=40, type="int"),
+# VariableModel(name="samp_rate", value=2000000.0, type="float"),
+# ]
+```
+
+### Get a Variable
+
+```python
+get_variable(name="freq")
+# Returns: 101100000.0
+```
+
+### Set a Variable
+
+```python
+set_variable(name="freq", value=98.5e6)
+# Returns: True
+```
+
+
+
+## Thread-Safe Updates
+
+For multiple parameter changes:
+
+```python
+# Pause signal processing
+lock()
+
+# Make changes (no audio during this)
+set_variable(name="freq", value=102.7e6)
+set_variable(name="gain", value=35)
+set_variable(name="bandwidth", value=200e3)
+
+# Resume processing
+unlock()
+```
+
+The flowgraph buffers input during the lock and processes it on unlock.
+
+## Execution Control
+
+```python
+# Stop the flowgraph (tb.stop())
+stop()
+
+# Restart the flowgraph (tb.start())
+start()
+```
+
+
+
+## Check Connection Status
+
+```python
+get_status()
+# Returns RuntimeStatusModel:
+# connected: True
+# connection: ConnectionInfoModel(url="http://localhost:8080", ...)
+# containers: [ContainerModel(...), ...]
+```
+
+## Disconnect
+
+```python
+disconnect()
+# Closes XML-RPC connection (and ControlPort if active)
+```
+
+## Example: FM Band Scanner
+
+```python
+#!/usr/bin/env python3
+"""Scan FM broadcast band and measure signal strength."""
+
+import asyncio
+import time
+from fastmcp import Client
+
+FM_STATIONS = [88.1, 90.3, 94.7, 98.5, 101.1, 103.5, 107.9]
+
+async def scan_fm_band():
+ async with Client("gr-mcp") as client:
+ await client.call_tool("enable_runtime_mode", {})
+
+ # Launch with a signal strength indicator
+ await client.call_tool("launch_flowgraph", {
+ "flowgraph_path": "/tmp/fm_scanner.py",
+ "name": "fm-scanner",
+ "xmlrpc_port": 8080
+ })
+
+ time.sleep(3)
+ await client.call_tool("connect_to_container", {"name": "fm-scanner"})
+
+ # Scan each station
+ results = []
+ for freq_mhz in FM_STATIONS:
+ await client.call_tool("set_variable", {
+ "name": "freq",
+ "value": freq_mhz * 1e6
+ })
+ time.sleep(0.5) # Allow settling
+
+ # Read signal level (if flowgraph exposes it)
+ try:
+ level = await client.call_tool("get_variable", {"name": "signal_level"})
+ results.append((freq_mhz, level.data))
+ except Exception:
+ results.append((freq_mhz, None))
+
+ # Report findings
+ for freq, level in results:
+ status = f"{level:.1f} dB" if level else "no signal"
+ print(f"{freq:.1f} MHz: {status}")
+
+ # Cleanup
+ await client.call_tool("stop_flowgraph", {"name": "fm-scanner"})
+ await client.call_tool("remove_flowgraph", {"name": "fm-scanner"})
+
+if __name__ == "__main__":
+ asyncio.run(scan_fm_band())
+```
+
+## Error Handling
+
+Common errors and solutions:
+
+| Error | Cause | Solution |
+|-------|-------|----------|
+| "Not connected" | No active XML-RPC connection | Call `connect()` or `connect_to_container()` |
+| "Unknown variable" | Variable not exposed via XML-RPC | Check flowgraph has XML-RPC block with callback vars |
+| "Connection refused" | Container not running / wrong port | Verify with `list_containers()`, `get_status()` |
+| "Timeout" | Flowgraph unresponsive | Check logs with `get_container_logs()` |
+
+## Next Steps
+
+- [ControlPort Monitoring](/guides/controlport/) — Advanced monitoring with performance counters
+- [Code Coverage](/guides/code-coverage/) — Collect coverage from runtime tests
diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx
new file mode 100644
index 0000000..902d14e
--- /dev/null
+++ b/docs/src/content/docs/index.mdx
@@ -0,0 +1,116 @@
+---
+title: GR-MCP
+description: GNU Radio MCP Server for programmatic, automated, and AI-driven flowgraph control
+draft: false
+template: splash
+hero:
+ tagline: Build, run, and control GNU Radio flowgraphs through the Model Context Protocol
+ actions:
+ - text: Get Started
+ link: /getting-started/installation/
+ icon: right-arrow
+ - text: View on Git
+ link: https://git.supported.systems/MCP/gr-mcp
+ icon: external
+ variant: minimal
+---
+
+import { Card, CardGrid, Tabs, TabItem } from '@astrojs/starlight/components';
+
+## What is GR-MCP?
+
+GR-MCP is a [FastMCP](https://gofastmcp.com) server that exposes GNU Radio's flowgraph capabilities through the [Model Context Protocol](https://modelcontextprotocol.io). This enables:
+
+- **Programmatic flowgraph creation** — Build `.grc` files from code or automation
+- **Runtime control** — Adjust variables and monitor performance without restarting
+- **Docker isolation** — Run flowgraphs in containers with automatic VNC/X11 support
+- **OOT module management** — Install and combine Out-of-Tree modules on demand
+
+
+
+ Create blocks, connect ports, validate flowgraphs, and generate Python code
+
+
+ Launch containers, control XML-RPC variables, monitor ControlPort knobs
+
+
+ Curated catalog with gr-osmosdr, gr-satellites, gr-lora_sdr, and more
+
+
+ Runtime tools load on-demand to minimize context usage
+
+
+
+## Quick Example
+
+
+
+```python
+# Create an FM receiver flowgraph
+make_block(block_type="osmosdr_source")
+make_block(block_type="low_pass_filter")
+make_block(block_type="analog_wfm_rcv")
+make_block(block_type="audio_sink")
+
+# Connect the signal chain
+connect_blocks("osmosdr_source_0", "0", "low_pass_filter_0", "0")
+connect_blocks("low_pass_filter_0", "0", "analog_wfm_rcv_0", "0")
+connect_blocks("analog_wfm_rcv_0", "0", "audio_sink_0", "0")
+
+# Validate and save
+validate_flowgraph()
+save_flowgraph("/tmp/fm_receiver.grc")
+```
+
+
+```python
+# Launch flowgraph in Docker container
+launch_flowgraph(
+ flowgraph_path="/tmp/fm_receiver.py",
+ name="fm-radio",
+ xmlrpc_port=8080,
+ enable_vnc=True
+)
+
+# Connect and tune
+connect_to_container("fm-radio")
+set_variable("freq", 101.1e6)
+
+# Capture display
+capture_screenshot("fm-radio")
+```
+
+
+```python
+# Check what's available
+# Resource: oot://directory
+
+# Install gr-lora_sdr for LoRa reception
+install_oot_module(
+ git_url="https://github.com/tapparelj/gr-lora_sdr",
+ branch="master"
+)
+
+# Launch with the new module
+launch_flowgraph(
+ flowgraph_path="lora_rx.py",
+ image="gnuradio-lora_sdr-runtime:latest"
+)
+```
+
+
+
+## Getting Started
+
+
+
+ Set up GR-MCP with `uv` and GNU Radio
+
+ [Read the guide →](/getting-started/installation/)
+
+
+ Build and validate a complete flowgraph
+
+ [Follow the tutorial →](/getting-started/first-flowgraph/)
+
+
diff --git a/docs/src/content/docs/reference/docker-images.mdx b/docs/src/content/docs/reference/docker-images.mdx
new file mode 100644
index 0000000..dc1172f
--- /dev/null
+++ b/docs/src/content/docs/reference/docker-images.mdx
@@ -0,0 +1,204 @@
+---
+title: Docker Images
+description: Reference for GR-MCP Docker images
+draft: false
+---
+
+import { Aside, Tabs, TabItem } from '@astrojs/starlight/components';
+
+GR-MCP uses Docker images for running flowgraphs in isolation with headless GUI support.
+
+## Base Images
+
+### `gnuradio-runtime:latest`
+
+The base runtime image for running flowgraphs.
+
+**Includes:**
+- GNU Radio 3.10.x
+- Xvfb (virtual framebuffer for headless X11)
+- VNC server (optional visual debugging)
+- ImageMagick (screenshot capture)
+- 12 preinstalled OOT modules (osmosdr, satellites, gsm, etc.)
+
+**Build:**
+```bash
+docker build -f docker/Dockerfile.gnuradio-runtime \
+ -t gnuradio-runtime:latest docker/
+```
+
+### `gnuradio-coverage:latest`
+
+Extended runtime image with Python coverage support.
+
+**Includes:**
+- Everything in `gnuradio-runtime`
+- `python3-coverage` package
+
+**Build:**
+```bash
+docker build -f docker/Dockerfile.gnuradio-coverage \
+ -t gnuradio-coverage:latest docker/
+```
+
+## OOT Module Images
+
+When you install an OOT module via `install_oot_module()`, GR-MCP creates a new image.
+
+### Naming Convention
+
+```
+gnuradio-{module_name}-runtime:latest
+```
+
+**Examples:**
+- `gnuradio-lora_sdr-runtime:latest`
+- `gnuradio-adsb-runtime:latest`
+- `gnuradio-ieee802_11-runtime:latest`
+
+### Build Process
+
+1. Clones the git repository
+2. Builds with cmake in a multi-stage Docker build
+3. Installs into the runtime image
+4. Tags with the module name
+
+## Combo Images
+
+For flowgraphs requiring multiple OOT modules, GR-MCP creates combo images.
+
+### Naming Convention
+
+```
+gr-combo-{module1}-{module2}-...:latest
+```
+
+Modules are sorted alphabetically in the name.
+
+**Examples:**
+- `gr-combo-adsb-lora_sdr:latest`
+- `gr-combo-adsb-ieee802_11-lora_sdr:latest`
+
+### Combo Keys
+
+Combo images are tracked by a `combo_key`:
+
+```
+combo:{module1}+{module2}+...
+```
+
+**Examples:**
+- `combo:adsb+lora_sdr`
+- `combo:adsb+ieee802_11+lora_sdr`
+
+## Container Configuration
+
+When launching a flowgraph, containers are configured with:
+
+| Feature | Configuration |
+|---------|---------------|
+| Display | `DISPLAY=:99` (Xvfb) |
+| X11 | Xvfb runs on `:99` |
+| VNC | Port 5900 (if enabled) |
+| XML-RPC | Dynamic port mapping |
+| ControlPort | Port 9090 (if enabled) |
+| Workdir | `/flowgraph` |
+| Network | Host network mode |
+
+### Labels
+
+GR-MCP containers are tagged with labels for management:
+
+```
+gr-mcp=true
+gr-mcp-name={container_name}
+gr-mcp-xmlrpc-port={port}
+gr-mcp-vnc={enabled}
+gr-mcp-controlport={enabled}
+gr-mcp-controlport-port={port}
+gr-mcp-coverage={enabled}
+```
+
+### Volume Mounts
+
+| Host Path | Container Path | Purpose |
+|-----------|----------------|---------|
+| Flowgraph file | `/flowgraph/{name}.py` | Flowgraph script |
+| Coverage dir | `/coverage` | Coverage data output |
+
+## Hardware Access
+
+To access SDR hardware inside containers, pass device paths:
+
+
+
+```python
+launch_flowgraph(
+ flowgraph_path="...",
+ device_paths=["/dev/bus/usb"]
+)
+```
+Grants access to all USB devices (RTL-SDR, HackRF, Airspy, etc.)
+
+
+```python
+launch_flowgraph(
+ flowgraph_path="...",
+ device_paths=["/dev/ttyUSB0"]
+)
+```
+Grants access to specific serial ports (USRP, etc.)
+
+
+```python
+launch_flowgraph(
+ flowgraph_path="...",
+ device_paths=["/dev/bus/usb", "/dev/ttyUSB0"]
+)
+```
+
+
+
+
+
+## Image Management
+
+### List Images
+
+```python
+# List OOT images
+list_oot_images()
+
+# List combo images
+list_combo_images()
+```
+
+### Remove Images
+
+```python
+# Remove single-module image
+remove_oot_image(module_name="lora_sdr")
+
+# Remove combo image
+remove_combo_image(combo_key="combo:adsb+lora_sdr")
+```
+
+### Docker Commands
+
+```bash
+# List all GR-MCP images
+docker images | grep -E "gnuradio-|gr-combo"
+
+# Remove all GR-MCP images
+docker images --format '{{.Repository}}:{{.Tag}}' | \
+ grep -E "gnuradio-|gr-combo" | xargs docker rmi
+
+# List all GR-MCP containers
+docker ps -a --filter "label=gr-mcp=true"
+
+# Remove all stopped GR-MCP containers
+docker container prune --filter "label=gr-mcp=true"
+```
diff --git a/docs/src/content/docs/reference/oot-catalog.mdx b/docs/src/content/docs/reference/oot-catalog.mdx
new file mode 100644
index 0000000..7a2ae50
--- /dev/null
+++ b/docs/src/content/docs/reference/oot-catalog.mdx
@@ -0,0 +1,249 @@
+---
+title: OOT Catalog
+description: Curated catalog of GNU Radio Out-of-Tree modules
+draft: false
+---
+
+import { Tabs, TabItem, Aside } from '@astrojs/starlight/components';
+
+GR-MCP includes a curated catalog of 24 OOT modules. 12 are preinstalled in the base
+runtime image; 12 can be installed on-demand.
+
+## Accessing the Catalog
+
+The catalog is exposed as MCP resources:
+
+```python
+# List all modules
+# Resource: oot://directory
+
+# Get details for a specific module
+# Resource: oot://directory/lora_sdr
+```
+
+## Preinstalled Modules
+
+These modules are included in `gnuradio-runtime:latest`:
+
+### Hardware
+
+| Module | Description |
+|--------|-------------|
+| **osmosdr** | Hardware source/sink for RTL-SDR, Airspy, HackRF, and more |
+| **funcube** | Funcube Dongle Pro/Pro+ controller and source block |
+| **hpsdr** | OpenHPSDR Protocol 1 interface for HPSDR hardware |
+| **limesdr** | LimeSDR source/sink blocks (LMS7002M) |
+
+### Satellite
+
+| Module | Description |
+|--------|-------------|
+| **satellites** | Satellite telemetry decoders (AX.25, CCSDS, AO-73, etc.) |
+| **satnogs** | SatNOGS satellite ground station decoders and deframers |
+
+### Broadcast
+
+| Module | Description |
+|--------|-------------|
+| **rds** | FM RDS/RBDS (Radio Data System) decoder |
+
+### Cellular
+
+| Module | Description |
+|--------|-------------|
+| **gsm** | GSM/GPRS burst receiver and channel decoder |
+
+### Aviation
+
+| Module | Description |
+|--------|-------------|
+| **air_modes** | Mode-S/ADS-B aircraft transponder decoder (1090 MHz) |
+
+### Analysis & Visualization
+
+| Module | Description |
+|--------|-------------|
+| **iqbal** | Blind IQ imbalance estimator and correction |
+| **radar** | Radar signal processing toolbox (FMCW, OFDM radar) |
+| **fosphor** | GPU-accelerated real-time spectrum display (OpenCL) |
+
+## Installable Modules
+
+Install these with `install_oot_module()`:
+
+### IoT
+
+#### gr-lora_sdr
+
+LoRa PHY transceiver (CSS modulation/demodulation)
+
+```python
+install_oot_module(
+ git_url="https://github.com/tapparelj/gr-lora_sdr",
+ branch="master"
+)
+```
+
+#### gr-ieee802_15_4
+
+IEEE 802.15.4 (Zigbee) O-QPSK transceiver
+
+```python
+install_oot_module(
+ git_url="https://github.com/bastibl/gr-ieee802-15-4",
+ branch="maint-3.10",
+ build_deps=["castxml"]
+)
+```
+
+### WiFi
+
+#### gr-ieee802_11
+
+IEEE 802.11a/g/p OFDM transceiver
+
+```python
+install_oot_module(
+ git_url="https://github.com/bastibl/gr-ieee802-11",
+ branch="maint-3.10",
+ build_deps=["castxml"]
+)
+```
+
+### Aviation
+
+#### gr-adsb
+
+ADS-B (1090 MHz) aircraft transponder decoder
+
+```python
+install_oot_module(
+ git_url="https://github.com/mhostetter/gr-adsb",
+ branch="maint-3.10"
+)
+```
+
+### Satellite
+
+#### gr-iridium
+
+Iridium satellite burst detector and demodulator
+
+```python
+install_oot_module(
+ git_url="https://github.com/muccc/gr-iridium",
+ branch="master"
+)
+```
+
+#### gr-leo
+
+LEO satellite channel simulator (Doppler, path loss, atmosphere)
+
+```python
+install_oot_module(
+ git_url="https://gitlab.com/librespacefoundation/gr-leo",
+ branch="gnuradio-3.10"
+)
+```
+
+### Broadcast
+
+#### gr-dab
+
+DAB/DAB+ digital audio broadcast receiver
+
+```python
+install_oot_module(
+ git_url="https://github.com/hboeglen/gr-dab",
+ branch="maint-3.10",
+ build_deps=["autoconf", "automake", "libtool", "libfaad-dev"],
+ cmake_args=["-DENABLE_DOXYGEN=OFF"]
+)
+```
+
+#### gr-nrsc5
+
+HD Radio (NRSC-5) digital broadcast decoder
+
+```python
+install_oot_module(
+ git_url="https://github.com/argilo/gr-nrsc5",
+ branch="master",
+ build_deps=["autoconf", "automake", "libtool"]
+)
+```
+
+#### gr-dl5eu
+
+DVB-T OFDM synchronization and TPS decoder
+
+```python
+install_oot_module(
+ git_url="https://github.com/dl5eu/gr-dl5eu",
+ branch="main"
+)
+```
+
+### Utility
+
+#### gr-foo
+
+Wireshark PCAP connector, burst tagger, periodic msg source
+
+```python
+install_oot_module(
+ git_url="https://github.com/bastibl/gr-foo",
+ branch="maint-3.10",
+ build_deps=["castxml"]
+)
+```
+
+### Optical
+
+#### gr-owc
+
+Optical Wireless Communication channel simulation and modulation
+
+```python
+install_oot_module(
+ git_url="https://github.com/UCaNLabUMB/gr-owc",
+ branch="main"
+)
+```
+
+### Analysis
+
+#### gr-inspector
+
+Signal analysis toolbox (energy detection, OFDM estimation)
+
+
+
+```python
+install_oot_module(
+ git_url="https://github.com/gnuradio/gr-inspector",
+ branch="master",
+ build_deps=["qtbase5-dev", "libqwt-qt5-dev"]
+)
+```
+
+## Module Categories Summary
+
+| Category | Preinstalled | Installable |
+|----------|--------------|-------------|
+| Hardware | 4 | 0 |
+| Satellite | 2 | 2 |
+| Cellular | 1 | 0 |
+| Broadcast | 1 | 3 |
+| Aviation | 1 | 1 |
+| IoT | 0 | 2 |
+| WiFi | 0 | 1 |
+| Analysis | 2 | 1 |
+| Visualization | 1 | 0 |
+| Utility | 0 | 1 |
+| Optical | 0 | 1 |
+| **Total** | **12** | **12** |
diff --git a/docs/src/content/docs/reference/tools-overview.mdx b/docs/src/content/docs/reference/tools-overview.mdx
new file mode 100644
index 0000000..6f21d74
--- /dev/null
+++ b/docs/src/content/docs/reference/tools-overview.mdx
@@ -0,0 +1,171 @@
+---
+title: Tools Overview
+description: Complete reference of all GR-MCP tools
+draft: false
+---
+
+import { CardGrid, Card, Aside } from '@astrojs/starlight/components';
+
+GR-MCP exposes tools in two groups: **platform tools** (always available) for flowgraph
+design, and **runtime tools** (loaded on demand) for container control.
+
+## Tool Organization
+
+
+
+ Always available for flowgraph building, validation, and code generation.
+ No Docker required.
+
+
+ Loaded via `enable_runtime_mode()`. Requires Docker for container features.
+
+
+
+## Platform Tools (Always Available)
+
+### Flowgraph Management
+
+| Tool | Description |
+|------|-------------|
+| [`get_blocks`](/reference/tools/flowgraph#get_blocks) | List all blocks in the flowgraph |
+| [`make_block`](/reference/tools/flowgraph#make_block) | Create a new block |
+| [`remove_block`](/reference/tools/flowgraph#remove_block) | Remove a block |
+| [`save_flowgraph`](/reference/tools/flowgraph#save_flowgraph) | Save to `.grc` file |
+| [`load_flowgraph`](/reference/tools/flowgraph#load_flowgraph) | Load from `.grc` file |
+| [`get_flowgraph_options`](/reference/tools/flowgraph#get_flowgraph_options) | Get flowgraph metadata |
+| [`set_flowgraph_options`](/reference/tools/flowgraph#set_flowgraph_options) | Set flowgraph metadata |
+| [`export_flowgraph_data`](/reference/tools/flowgraph#export_flowgraph_data) | Export as dict |
+| [`import_flowgraph_data`](/reference/tools/flowgraph#import_flowgraph_data) | Import from dict |
+
+### Block Management
+
+| Tool | Description |
+|------|-------------|
+| [`get_block_params`](/reference/tools/blocks#get_block_params) | List block parameters |
+| [`set_block_params`](/reference/tools/blocks#set_block_params) | Set block parameters |
+| [`get_block_sources`](/reference/tools/blocks#get_block_sources) | List output ports |
+| [`get_block_sinks`](/reference/tools/blocks#get_block_sinks) | List input ports |
+| [`bypass_block`](/reference/tools/blocks#bypass_block) | Bypass a block |
+| [`unbypass_block`](/reference/tools/blocks#unbypass_block) | Unbypass a block |
+
+### Connection Management
+
+| Tool | Description |
+|------|-------------|
+| [`get_connections`](/reference/tools/connections#get_connections) | List all connections |
+| [`connect_blocks`](/reference/tools/connections#connect_blocks) | Connect two blocks |
+| [`disconnect_blocks`](/reference/tools/connections#disconnect_blocks) | Disconnect two blocks |
+
+### Validation
+
+| Tool | Description |
+|------|-------------|
+| [`validate_block`](/reference/tools/validation#validate_block) | Validate a single block |
+| [`validate_flowgraph`](/reference/tools/validation#validate_flowgraph) | Validate entire flowgraph |
+| [`get_all_errors`](/reference/tools/validation#get_all_errors) | Get all validation errors |
+
+### Platform
+
+| Tool | Description |
+|------|-------------|
+| [`get_all_available_blocks`](/reference/tools/platform#get_all_available_blocks) | List all block types |
+| [`search_blocks`](/reference/tools/platform#search_blocks) | Search blocks by keyword |
+| [`get_block_categories`](/reference/tools/platform#get_block_categories) | List block categories |
+| [`load_oot_blocks`](/reference/tools/platform#load_oot_blocks) | Load OOT block paths |
+| [`add_block_path`](/reference/tools/platform#add_block_path) | Add a block path |
+| [`get_block_paths`](/reference/tools/platform#get_block_paths) | List block paths |
+
+### Code Generation
+
+| Tool | Description |
+|------|-------------|
+| [`generate_code`](/reference/tools/codegen#generate_code) | Generate Python code |
+| [`evaluate_expression`](/reference/tools/codegen#evaluate_expression) | Evaluate Python expression |
+| [`create_embedded_python_block`](/reference/tools/codegen#create_embedded_python_block) | Create embedded Python block |
+
+## Runtime Tools (Enable with `enable_runtime_mode()`)
+
+
+
+### Runtime Mode Control
+
+| Tool | Description |
+|------|-------------|
+| [`get_runtime_mode`](/reference/tools/runtime-mode#get_runtime_mode) | Check runtime mode status |
+| [`enable_runtime_mode`](/reference/tools/runtime-mode#enable_runtime_mode) | Enable runtime tools |
+| [`disable_runtime_mode`](/reference/tools/runtime-mode#disable_runtime_mode) | Disable runtime tools |
+| [`get_client_capabilities`](/reference/tools/runtime-mode#get_client_capabilities) | Get MCP client capabilities |
+| [`list_client_roots`](/reference/tools/runtime-mode#list_client_roots) | List client root directories |
+
+### Docker Container Management
+
+| Tool | Description |
+|------|-------------|
+| [`launch_flowgraph`](/reference/tools/docker#launch_flowgraph) | Launch in Docker container |
+| [`list_containers`](/reference/tools/docker#list_containers) | List running containers |
+| [`stop_flowgraph`](/reference/tools/docker#stop_flowgraph) | Stop a container |
+| [`remove_flowgraph`](/reference/tools/docker#remove_flowgraph) | Remove a container |
+| [`capture_screenshot`](/reference/tools/docker#capture_screenshot) | Capture GUI screenshot |
+| [`get_container_logs`](/reference/tools/docker#get_container_logs) | Get container logs |
+
+### XML-RPC Control
+
+| Tool | Description |
+|------|-------------|
+| [`connect`](/reference/tools/xmlrpc#connect) | Connect to XML-RPC URL |
+| [`connect_to_container`](/reference/tools/xmlrpc#connect_to_container) | Connect by container name |
+| [`disconnect`](/reference/tools/xmlrpc#disconnect) | Disconnect |
+| [`get_status`](/reference/tools/xmlrpc#get_status) | Get connection status |
+| [`list_variables`](/reference/tools/xmlrpc#list_variables) | List exposed variables |
+| [`get_variable`](/reference/tools/xmlrpc#get_variable) | Get variable value |
+| [`set_variable`](/reference/tools/xmlrpc#set_variable) | Set variable value |
+| [`start`](/reference/tools/xmlrpc#start) | Start flowgraph |
+| [`stop`](/reference/tools/xmlrpc#stop) | Stop flowgraph |
+| [`lock`](/reference/tools/xmlrpc#lock) | Lock for updates |
+| [`unlock`](/reference/tools/xmlrpc#unlock) | Unlock after updates |
+
+### ControlPort/Thrift
+
+| Tool | Description |
+|------|-------------|
+| [`connect_controlport`](/reference/tools/controlport#connect_controlport) | Connect to ControlPort |
+| [`connect_to_container_controlport`](/reference/tools/controlport#connect_to_container_controlport) | Connect by container |
+| [`disconnect_controlport`](/reference/tools/controlport#disconnect_controlport) | Disconnect ControlPort |
+| [`get_knobs`](/reference/tools/controlport#get_knobs) | Get knob values |
+| [`set_knobs`](/reference/tools/controlport#set_knobs) | Set knob values |
+| [`get_knob_properties`](/reference/tools/controlport#get_knob_properties) | Get knob metadata |
+| [`get_performance_counters`](/reference/tools/controlport#get_performance_counters) | Get perf metrics |
+| [`post_message`](/reference/tools/controlport#post_message) | Send PMT message |
+
+### Code Coverage
+
+| Tool | Description |
+|------|-------------|
+| [`collect_coverage`](/reference/tools/coverage#collect_coverage) | Collect coverage data |
+| [`generate_coverage_report`](/reference/tools/coverage#generate_coverage_report) | Generate report |
+| [`combine_coverage`](/reference/tools/coverage#combine_coverage) | Combine multiple runs |
+| [`delete_coverage`](/reference/tools/coverage#delete_coverage) | Delete coverage data |
+
+### OOT Module Installation
+
+| Tool | Description |
+|------|-------------|
+| [`detect_oot_modules`](/reference/tools/oot#detect_oot_modules) | Detect OOT dependencies |
+| [`install_oot_module`](/reference/tools/oot#install_oot_module) | Install OOT module |
+| [`list_oot_images`](/reference/tools/oot#list_oot_images) | List installed images |
+| [`remove_oot_image`](/reference/tools/oot#remove_oot_image) | Remove OOT image |
+| [`build_multi_oot_image`](/reference/tools/oot#build_multi_oot_image) | Build combo image |
+| [`list_combo_images`](/reference/tools/oot#list_combo_images) | List combo images |
+| [`remove_combo_image`](/reference/tools/oot#remove_combo_image) | Remove combo image |
+
+## MCP Resources
+
+In addition to tools, GR-MCP exposes resources for OOT module discovery:
+
+| URI | Description |
+|-----|-------------|
+| `oot://directory` | Index of all OOT modules in the catalog |
+| `oot://directory/{name}` | Detailed info for a specific module |
diff --git a/docs/src/content/docs/reference/tools/blocks.mdx b/docs/src/content/docs/reference/tools/blocks.mdx
new file mode 100644
index 0000000..3045e58
--- /dev/null
+++ b/docs/src/content/docs/reference/tools/blocks.mdx
@@ -0,0 +1,168 @@
+---
+title: Block Tools
+description: Tools for block parameter and port management
+draft: false
+---
+
+Tools for managing block parameters and inspecting input/output ports.
+
+## `get_block_params`
+
+Get all parameters for a block.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `block_name` | `str` | - | Name of the block |
+
+### Returns
+
+**Type:** `list[ParamModel]`
+
+List of parameters with key, value, name, and type.
+
+### Example
+
+```python
+params = get_block_params(block_name="osmosdr_source_0")
+# Returns: [
+# ParamModel(key="freq", value="101.1e6", name="Ch0: Frequency", dtype="real"),
+# ParamModel(key="sample_rate", value="2e6", name="Sample Rate", dtype="real"),
+# ...
+# ]
+```
+
+---
+
+## `set_block_params`
+
+Set parameters on a block.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `block_name` | `str` | - | Name of the block |
+| `params` | `dict[str, Any]` | - | Dict of parameter key → value |
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+set_block_params(block_name="osmosdr_source_0", params={
+ "freq": "101.1e6",
+ "sample_rate": "2e6",
+ "gain": "40"
+})
+# Returns: True
+```
+
+---
+
+## `get_block_sources`
+
+Get output ports (sources) for a block.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `block_name` | `str` | - | Name of the block |
+
+### Returns
+
+**Type:** `list[PortModel]`
+
+List of output ports with key, name, and data type.
+
+### Example
+
+```python
+sources = get_block_sources(block_name="osmosdr_source_0")
+# Returns: [
+# PortModel(key="0", name="out", dtype="complex", direction="source")
+# ]
+```
+
+---
+
+## `get_block_sinks`
+
+Get input ports (sinks) for a block.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `block_name` | `str` | - | Name of the block |
+
+### Returns
+
+**Type:** `list[PortModel]`
+
+List of input ports with key, name, and data type.
+
+### Example
+
+```python
+sinks = get_block_sinks(block_name="low_pass_filter_0")
+# Returns: [
+# PortModel(key="0", name="in", dtype="complex", direction="sink")
+# ]
+```
+
+---
+
+## `bypass_block`
+
+Bypass a block (pass signal through without processing).
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `block_name` | `str` | - | Name of the block to bypass |
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+bypass_block(block_name="low_pass_filter_0")
+# Returns: True
+```
+
+---
+
+## `unbypass_block`
+
+Re-enable a bypassed block.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `block_name` | `str` | - | Name of the block to unbypass |
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+unbypass_block(block_name="low_pass_filter_0")
+# Returns: True
+```
diff --git a/docs/src/content/docs/reference/tools/codegen.mdx b/docs/src/content/docs/reference/tools/codegen.mdx
new file mode 100644
index 0000000..9b1b31f
--- /dev/null
+++ b/docs/src/content/docs/reference/tools/codegen.mdx
@@ -0,0 +1,147 @@
+---
+title: Code Generation
+description: Tools for generating Python code and evaluating expressions
+draft: false
+---
+
+Tools for generating executable Python code from flowgraphs.
+
+## `generate_code`
+
+Generate Python/C++ code from the current flowgraph.
+
+Unlike the `grcc` command-line tool, this does **not** block on validation errors.
+Validation warnings are included in the response for reference.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `output_dir` | `str` | `""` | Output directory (defaults to temp directory) |
+
+### Returns
+
+**Type:** `GeneratedCodeModel`
+
+Generated code result with path, validity, and warnings.
+
+### Example
+
+```python
+result = generate_code(output_dir="/tmp")
+# Returns: GeneratedCodeModel(
+# file_path="/tmp/fm_receiver.py",
+# is_valid=True,
+# warnings=[]
+# )
+
+# With validation warnings
+result = generate_code()
+# Returns: GeneratedCodeModel(
+# file_path="/tmp/tmpXXXXXX/fm_receiver.py",
+# is_valid=False,
+# warnings=["audio_sink_0: Sample rate mismatch"]
+# )
+```
+
+### Notes
+
+- Generated code includes XML-RPC server if the flowgraph has XML-RPC variables
+- Output type (Python/C++) depends on `generate_options` in flowgraph options
+- File name is derived from flowgraph title
+
+---
+
+## `evaluate_expression`
+
+Evaluate a Python expression in the flowgraph's namespace.
+
+Useful for testing parameter expressions before setting them.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `expr` | `str` | - | Python expression to evaluate |
+
+### Returns
+
+**Type:** `Any`
+
+Result of the expression evaluation.
+
+### Example
+
+```python
+# Arithmetic
+result = evaluate_expression("101.1e6 + 200e3")
+# Returns: 101300000.0
+
+# Using numpy (available in flowgraph namespace)
+result = evaluate_expression("np.pi * 2")
+# Returns: 6.283185307179586
+
+# Complex expressions
+result = evaluate_expression("int(2e6 / 10)")
+# Returns: 200000
+```
+
+---
+
+## `create_embedded_python_block`
+
+Create an embedded Python block from source code.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `source_code` | `str` | - | Python source code for the block |
+| `block_name` | `str \| None` | `None` | Optional custom block name |
+
+### Returns
+
+**Type:** `str`
+
+The assigned block name.
+
+### Example
+
+```python
+source = '''
+import numpy as np
+from gnuradio import gr
+
+class my_multiplier(gr.sync_block):
+ """Multiply input by a constant."""
+
+ def __init__(self, multiplier=2.0):
+ gr.sync_block.__init__(
+ self,
+ name="my_multiplier",
+ in_sig=[np.complex64],
+ out_sig=[np.complex64]
+ )
+ self.multiplier = multiplier
+
+ def work(self, input_items, output_items):
+ output_items[0][:] = input_items[0] * self.multiplier
+ return len(output_items[0])
+'''
+
+name = create_embedded_python_block(source_code=source)
+# Returns: "epy_block_0"
+
+# With custom name
+name = create_embedded_python_block(
+ source_code=source,
+ block_name="multiplier"
+)
+# Returns: "multiplier_0"
+```
+
+### Notes
+
+- Embedded blocks must inherit from `gr.sync_block`, `gr.decim_block`, or `gr.interp_block`
+- The block class must define `__init__` and `work` methods
+- Use `set_block_params()` to configure the block after creation
diff --git a/docs/src/content/docs/reference/tools/connections.mdx b/docs/src/content/docs/reference/tools/connections.mdx
new file mode 100644
index 0000000..f3cd79c
--- /dev/null
+++ b/docs/src/content/docs/reference/tools/connections.mdx
@@ -0,0 +1,113 @@
+---
+title: Connection Tools
+description: Tools for wiring blocks together
+draft: false
+---
+
+Tools for managing connections (wires) between blocks.
+
+## `get_connections`
+
+List all connections in the flowgraph.
+
+### Returns
+
+**Type:** `list[ConnectionModel]`
+
+List of connections with source/sink block names and port keys.
+
+### Example
+
+```python
+connections = get_connections()
+# Returns: [
+# ConnectionModel(
+# source_block="osmosdr_source_0", source_port="0",
+# sink_block="low_pass_filter_0", sink_port="0"
+# ),
+# ...
+# ]
+```
+
+---
+
+## `connect_blocks`
+
+Connect two blocks by their ports.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `source_block_name` | `str` | - | Name of source block |
+| `sink_block_name` | `str` | - | Name of sink block |
+| `source_port_name` | `str` | - | Port key on source block |
+| `sink_port_name` | `str` | - | Port key on sink block |
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+connect_blocks(
+ source_block_name="osmosdr_source_0",
+ sink_block_name="low_pass_filter_0",
+ source_port_name="0",
+ sink_port_name="0"
+)
+# Returns: True
+```
+
+### Notes
+
+- Use `get_block_sources()` and `get_block_sinks()` to discover available ports
+- Most blocks use port `"0"` for their primary input/output
+- Port types must be compatible (e.g., both complex, both float)
+
+---
+
+## `disconnect_blocks`
+
+Disconnect two blocks.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `source_port` | `PortModel` | - | Source port to disconnect |
+| `sink_port` | `PortModel` | - | Sink port to disconnect |
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+# Get the connection to disconnect
+connections = get_connections()
+conn = connections[0]
+
+disconnect_blocks(
+ source_port=PortModel(
+ parent=conn.source_block,
+ key=conn.source_port,
+ name="out",
+ dtype="complex",
+ direction="source"
+ ),
+ sink_port=PortModel(
+ parent=conn.sink_block,
+ key=conn.sink_port,
+ name="in",
+ dtype="complex",
+ direction="sink"
+ )
+)
+```
diff --git a/docs/src/content/docs/reference/tools/controlport.mdx b/docs/src/content/docs/reference/tools/controlport.mdx
new file mode 100644
index 0000000..24f5c22
--- /dev/null
+++ b/docs/src/content/docs/reference/tools/controlport.mdx
@@ -0,0 +1,268 @@
+---
+title: ControlPort Tools
+description: Tools for ControlPort/Thrift connection and monitoring
+draft: false
+---
+
+import { Aside } from '@astrojs/starlight/components';
+
+Tools for advanced runtime control via ControlPort (Thrift protocol).
+
+
+
+## `connect_controlport`
+
+Connect to a GNU Radio ControlPort/Thrift endpoint.
+
+ControlPort provides richer functionality than XML-RPC:
+- Native type support (complex numbers, vectors)
+- Performance counters (throughput, timing, buffer utilization)
+- Knob metadata (units, min/max, descriptions)
+- PMT message injection
+- Regex-based knob queries
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `host` | `str` | `"127.0.0.1"` | Hostname or IP address |
+| `port` | `int` | `9090` | ControlPort Thrift port |
+
+### Returns
+
+**Type:** `ThriftConnectionInfoModel`
+
+Connection details.
+
+### Example
+
+```python
+connect_controlport(host="127.0.0.1", port=9090)
+# Returns: ThriftConnectionInfoModel(
+# host="127.0.0.1",
+# port=9090,
+# container_name=None
+# )
+```
+
+---
+
+## `connect_to_container_controlport`
+
+Connect to a flowgraph's ControlPort by container name.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `name` | `str` | - | Container name |
+
+### Returns
+
+**Type:** `ThriftConnectionInfoModel`
+
+Connection details.
+
+### Example
+
+```python
+connect_to_container_controlport(name="fm-profiled")
+```
+
+
+
+---
+
+## `disconnect_controlport`
+
+Disconnect from the current ControlPort endpoint.
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+disconnect_controlport()
+# Returns: True
+```
+
+---
+
+## `get_knobs`
+
+Get ControlPort knobs, optionally filtered by regex pattern.
+
+Knobs are named using the pattern: `block_alias::varname`
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `pattern` | `str` | `""` | Regex pattern for filtering (empty = all) |
+
+### Returns
+
+**Type:** `list[KnobModel]`
+
+List of knobs with name, value, and type.
+
+### Example
+
+```python
+# Get all knobs
+knobs = get_knobs(pattern="")
+
+# Filter by pattern
+knobs = get_knobs(pattern=".*frequency.*")
+# Returns: [KnobModel(name="sig_source_0::frequency", value=1000.0, ...)]
+
+# Get knobs for a specific block
+knobs = get_knobs(pattern="osmosdr_source_0::.*")
+```
+
+---
+
+## `set_knobs`
+
+Set multiple ControlPort knobs atomically.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `knobs` | `dict[str, Any]` | - | Dict mapping knob names to new values |
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+set_knobs({
+ "sig_source_0::frequency": 1500.0,
+ "sig_source_0::amplitude": 0.8
+})
+# Returns: True
+```
+
+---
+
+## `get_knob_properties`
+
+Get metadata (units, min/max, description) for specified knobs.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `names` | `list[str]` | - | Knob names to query (empty = all) |
+
+### Returns
+
+**Type:** `list[KnobPropertiesModel]`
+
+Knob metadata.
+
+### Example
+
+```python
+props = get_knob_properties(names=["sig_source_0::frequency"])
+# Returns: [KnobPropertiesModel(
+# name="sig_source_0::frequency",
+# units="Hz",
+# min=0.0,
+# max=1e9,
+# description="Output signal frequency"
+# )]
+```
+
+---
+
+## `get_performance_counters`
+
+Get performance metrics for blocks via ControlPort.
+
+Requires the flowgraph to be launched with `enable_controlport=True` and
+`enable_perf_counters=True` (default).
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `block` | `str \| None` | `None` | Block alias to filter (None = all blocks) |
+
+### Returns
+
+**Type:** `list[PerfCounterModel]`
+
+Performance metrics.
+
+### Example
+
+```python
+counters = get_performance_counters()
+# Returns: [PerfCounterModel(
+# block="low_pass_filter_0",
+# nproduced=1048576,
+# nconsumed=10485760,
+# avg_work_time_us=45.3,
+# avg_throughput=23.2e6,
+# pc_input_buffers_full=0.85,
+# pc_output_buffers_full=0.12
+# ), ...]
+
+# Filter by block
+counters = get_performance_counters(block="low_pass_filter_0")
+```
+
+### Metrics
+
+| Metric | Description |
+|--------|-------------|
+| `nproduced` | Items produced by block |
+| `nconsumed` | Items consumed by block |
+| `avg_work_time_us` | Average time in `work()` function |
+| `avg_throughput` | Items per second |
+| `pc_input_buffers_full` | Input buffer utilization (0-1) |
+| `pc_output_buffers_full` | Output buffer utilization (0-1) |
+
+---
+
+## `post_message`
+
+Send a PMT message to a block's message port via ControlPort.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `block` | `str` | - | Block alias (e.g., "msg_sink0") |
+| `port` | `str` | - | Message port name (e.g., "in") |
+| `message` | `Any` | - | Message to send (converted to PMT if needed) |
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+# Send a string message
+post_message(block="pdu_sink_0", port="pdus", message="hello")
+
+# Send a dict (converted to PMT dict)
+post_message(block="command_handler_0", port="command", message={"freq": 1e6})
+```
diff --git a/docs/src/content/docs/reference/tools/coverage.mdx b/docs/src/content/docs/reference/tools/coverage.mdx
new file mode 100644
index 0000000..0681eff
--- /dev/null
+++ b/docs/src/content/docs/reference/tools/coverage.mdx
@@ -0,0 +1,158 @@
+---
+title: Coverage Tools
+description: Tools for collecting Python code coverage
+draft: false
+---
+
+import { Aside } from '@astrojs/starlight/components';
+
+Tools for collecting Python code coverage from containerized flowgraphs.
+
+
+
+## `collect_coverage`
+
+Collect coverage data from a stopped container.
+
+Combines any parallel coverage files and returns a summary.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `name` | `str` | - | Container name |
+
+### Returns
+
+**Type:** `CoverageDataModel`
+
+Coverage summary.
+
+### Example
+
+```python
+data = collect_coverage(name="coverage-test")
+# Returns: CoverageDataModel(
+# container_name="coverage-test",
+# coverage_file="/tmp/gr-mcp-coverage/coverage-test/.coverage",
+# summary="Name Stmts Miss Cover\n...",
+# lines_covered=150,
+# lines_total=200,
+# coverage_percent=75.0
+# )
+```
+
+
+
+---
+
+## `generate_coverage_report`
+
+Generate a coverage report in the specified format.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `name` | `str` | - | Container name |
+| `format` | `str` | `"html"` | Report format: `html`, `xml`, or `json` |
+
+### Returns
+
+**Type:** `CoverageReportModel`
+
+Report location.
+
+### Example
+
+```python
+# HTML report
+report = generate_coverage_report(name="coverage-test", format="html")
+# Returns: CoverageReportModel(
+# container_name="coverage-test",
+# format="html",
+# report_path="/tmp/gr-mcp-coverage/coverage-test/htmlcov/index.html"
+# )
+
+# XML report (Cobertura format)
+report = generate_coverage_report(name="coverage-test", format="xml")
+# report_path="/tmp/gr-mcp-coverage/coverage-test/coverage.xml"
+
+# JSON report
+report = generate_coverage_report(name="coverage-test", format="json")
+# report_path="/tmp/gr-mcp-coverage/coverage-test/coverage.json"
+```
+
+---
+
+## `combine_coverage`
+
+Combine coverage data from multiple containers.
+
+Useful for aggregating coverage across a test suite.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `names` | `list[str]` | - | Container names to combine |
+
+### Returns
+
+**Type:** `CoverageDataModel`
+
+Combined coverage summary.
+
+### Example
+
+```python
+combined = combine_coverage(names=["test-1", "test-2", "test-3"])
+# Returns: CoverageDataModel(
+# container_name="combined",
+# coverage_file="/tmp/gr-mcp-coverage/combined/.coverage",
+# summary="...",
+# lines_covered=250,
+# lines_total=300,
+# coverage_percent=83.3
+# )
+```
+
+---
+
+## `delete_coverage`
+
+Delete coverage data.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `name` | `str \| None` | `None` | Delete specific container's coverage |
+| `older_than_days` | `int \| None` | `None` | Delete coverage older than N days |
+
+### Returns
+
+**Type:** `int`
+
+Number of coverage directories deleted.
+
+### Example
+
+```python
+# Delete specific container's coverage
+delete_coverage(name="coverage-test")
+# Returns: 1
+
+# Delete old coverage
+delete_coverage(older_than_days=7)
+# Returns: 5
+
+# Delete all coverage
+delete_coverage()
+# Returns: 10
+```
diff --git a/docs/src/content/docs/reference/tools/docker.mdx b/docs/src/content/docs/reference/tools/docker.mdx
new file mode 100644
index 0000000..2373d50
--- /dev/null
+++ b/docs/src/content/docs/reference/tools/docker.mdx
@@ -0,0 +1,212 @@
+---
+title: Docker Tools
+description: Tools for container lifecycle management
+draft: false
+---
+
+import { Aside } from '@astrojs/starlight/components';
+
+Tools for launching and managing flowgraphs in Docker containers.
+
+
+
+## `launch_flowgraph`
+
+Launch a flowgraph in a Docker container with Xvfb (headless X11).
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `flowgraph_path` | `str` | - | Path to the `.py` flowgraph file |
+| `name` | `str \| None` | `None` | Container name (defaults to `gr-{stem}`) |
+| `xmlrpc_port` | `int` | `0` | XML-RPC port (0 = auto-allocate) |
+| `enable_vnc` | `bool` | `False` | Enable VNC server for visual debugging |
+| `enable_coverage` | `bool` | `False` | Enable Python code coverage collection |
+| `enable_controlport` | `bool` | `False` | Enable ControlPort/Thrift |
+| `controlport_port` | `int` | `9090` | ControlPort port |
+| `enable_perf_counters` | `bool` | `True` | Enable performance counters |
+| `device_paths` | `list[str] \| None` | `None` | Host device paths to pass through |
+| `image` | `str \| None` | `None` | Docker image to use |
+| `auto_image` | `bool` | `False` | Auto-detect OOT modules and build image |
+
+### Returns
+
+**Type:** `ContainerModel`
+
+Container information including name, status, and ports.
+
+### Example
+
+```python
+# Basic launch
+container = launch_flowgraph(
+ flowgraph_path="/tmp/fm_receiver.py",
+ name="fm-radio"
+)
+
+# With XML-RPC and VNC
+container = launch_flowgraph(
+ flowgraph_path="/tmp/fm_receiver.py",
+ name="fm-radio",
+ xmlrpc_port=8080,
+ enable_vnc=True
+)
+
+# With SDR hardware access
+container = launch_flowgraph(
+ flowgraph_path="/tmp/fm_receiver.py",
+ name="fm-hardware",
+ device_paths=["/dev/bus/usb"]
+)
+
+# Auto-detect OOT modules
+container = launch_flowgraph(
+ flowgraph_path="/tmp/lora_rx.py",
+ auto_image=True
+)
+```
+
+---
+
+## `list_containers`
+
+List all GR-MCP managed containers.
+
+### Returns
+
+**Type:** `list[ContainerModel]`
+
+List of containers with status and port information.
+
+### Example
+
+```python
+containers = list_containers()
+# Returns: [
+# ContainerModel(
+# name="fm-radio",
+# status="running",
+# xmlrpc_port=32768,
+# vnc_port=5900,
+# ...
+# ),
+# ...
+# ]
+```
+
+---
+
+## `stop_flowgraph`
+
+Stop a running flowgraph container (graceful shutdown).
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `name` | `str` | - | Container name |
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+stop_flowgraph(name="fm-radio")
+# Returns: True
+```
+
+
+
+---
+
+## `remove_flowgraph`
+
+Remove a flowgraph container.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `name` | `str` | - | Container name |
+| `force` | `bool` | `False` | Force remove even if running |
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+# Normal removal (must be stopped first)
+remove_flowgraph(name="fm-radio")
+
+# Force removal
+remove_flowgraph(name="fm-radio", force=True)
+```
+
+---
+
+## `capture_screenshot`
+
+Capture a screenshot of the flowgraph's QT GUI.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `name` | `str \| None` | `None` | Container name (uses active if not specified) |
+
+### Returns
+
+**Type:** `ScreenshotModel`
+
+Screenshot information with path and dimensions.
+
+### Example
+
+```python
+screenshot = capture_screenshot(name="fm-radio")
+# Returns: ScreenshotModel(
+# path="/tmp/gr-mcp-screenshots/fm-radio-2024-01-15-14-30-00.png",
+# width=1024,
+# height=768
+# )
+```
+
+---
+
+## `get_container_logs`
+
+Get logs from a flowgraph container.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `name` | `str \| None` | `None` | Container name (uses active if not specified) |
+| `tail` | `int` | `100` | Number of lines to return |
+
+### Returns
+
+**Type:** `str`
+
+Container stdout/stderr output.
+
+### Example
+
+```python
+logs = get_container_logs(name="fm-radio", tail=50)
+# Returns: "Using Volk machine: avx2_64_mmx\nStarting flowgraph...\n..."
+```
diff --git a/docs/src/content/docs/reference/tools/flowgraph.mdx b/docs/src/content/docs/reference/tools/flowgraph.mdx
new file mode 100644
index 0000000..fbc180f
--- /dev/null
+++ b/docs/src/content/docs/reference/tools/flowgraph.mdx
@@ -0,0 +1,228 @@
+---
+title: Flowgraph Tools
+description: Tools for managing flowgraph structure
+draft: false
+---
+
+Tools for managing flowgraph structure: blocks, connections, save/load, and metadata.
+
+## `get_blocks`
+
+List all blocks currently in the flowgraph.
+
+### Returns
+
+**Type:** `list[BlockModel]`
+
+List of blocks with their names, keys, and states.
+
+### Example
+
+```python
+blocks = get_blocks()
+# Returns: [
+# BlockModel(name="osmosdr_source_0", key="osmosdr_source", ...),
+# BlockModel(name="low_pass_filter_0", key="low_pass_filter", ...),
+# ]
+```
+
+---
+
+## `make_block`
+
+Create a new block in the flowgraph.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `block_name` | `str` | - | Block type key (e.g., "osmosdr_source") |
+
+### Returns
+
+**Type:** `str`
+
+The unique name assigned to the block (e.g., "osmosdr_source_0").
+
+### Example
+
+```python
+name = make_block(block_name="osmosdr_source")
+# Returns: "osmosdr_source_0"
+```
+
+---
+
+## `remove_block`
+
+Remove a block from the flowgraph.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `block_name` | `str` | - | Name of the block to remove |
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+remove_block(block_name="osmosdr_source_0")
+# Returns: True
+```
+
+---
+
+## `save_flowgraph`
+
+Save the flowgraph to a `.grc` file.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `filepath` | `str` | - | Path to save the `.grc` file |
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+save_flowgraph(filepath="/tmp/my_flowgraph.grc")
+# Returns: True
+```
+
+---
+
+## `load_flowgraph`
+
+Load a flowgraph from a `.grc` file, replacing the current flowgraph.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `filepath` | `str` | - | Path to the `.grc` file |
+
+### Returns
+
+**Type:** `list[BlockModel]`
+
+List of blocks in the loaded flowgraph.
+
+### Example
+
+```python
+blocks = load_flowgraph(filepath="/tmp/my_flowgraph.grc")
+# Returns: [BlockModel(...), ...]
+```
+
+---
+
+## `get_flowgraph_options`
+
+Get the flowgraph-level options (title, author, generate_options, etc.).
+
+### Returns
+
+**Type:** `FlowgraphOptionsModel`
+
+Flowgraph metadata including title, author, category, description, and generate options.
+
+### Example
+
+```python
+options = get_flowgraph_options()
+# Returns: FlowgraphOptionsModel(
+# title="FM Receiver",
+# author="Your Name",
+# generate_options="qt_gui",
+# ...
+# )
+```
+
+---
+
+## `set_flowgraph_options`
+
+Set flowgraph-level options on the 'options' block.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `params` | `dict[str, Any]` | - | Options to set |
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+set_flowgraph_options(params={
+ "title": "FM Receiver",
+ "author": "Your Name",
+ "generate_options": "qt_gui"
+})
+# Returns: True
+```
+
+---
+
+## `export_flowgraph_data`
+
+Export the flowgraph as a nested dict (same format as `.grc` files).
+
+### Returns
+
+**Type:** `dict`
+
+Complete flowgraph data in GRC YAML format.
+
+### Example
+
+```python
+data = export_flowgraph_data()
+# Returns: {"options": {...}, "blocks": [...], "connections": [...]}
+```
+
+---
+
+## `import_flowgraph_data`
+
+Import flowgraph data from a dict, replacing current contents.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `data` | `dict` | - | Flowgraph data in GRC format |
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+import_flowgraph_data(data={
+ "options": {"title": "Test"},
+ "blocks": [...],
+ "connections": [...]
+})
+# Returns: True
+```
diff --git a/docs/src/content/docs/reference/tools/oot.mdx b/docs/src/content/docs/reference/tools/oot.mdx
new file mode 100644
index 0000000..2460339
--- /dev/null
+++ b/docs/src/content/docs/reference/tools/oot.mdx
@@ -0,0 +1,238 @@
+---
+title: OOT Tools
+description: Tools for OOT module detection and installation
+draft: false
+---
+
+import { Aside } from '@astrojs/starlight/components';
+
+Tools for detecting, installing, and managing Out-of-Tree (OOT) modules.
+
+
+
+## `detect_oot_modules`
+
+Detect which OOT modules a flowgraph requires.
+
+Analyzes `.py` or `.grc` files to find OOT module dependencies.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `flowgraph_path` | `str` | - | Path to a `.py` or `.grc` flowgraph file |
+
+### Returns
+
+**Type:** `OOTDetectionResult`
+
+Detection results with modules and recommended image.
+
+### Example
+
+```python
+result = detect_oot_modules(flowgraph_path="lora_rx.py")
+# Returns: OOTDetectionResult(
+# detected_modules=["lora_sdr"],
+# unknown_blocks=[],
+# recommended_image="gnuradio-lora_sdr-runtime:latest"
+# )
+
+# Multiple modules
+result = detect_oot_modules(flowgraph_path="multi_protocol_rx.py")
+# Returns: OOTDetectionResult(
+# detected_modules=["lora_sdr", "adsb"],
+# unknown_blocks=[],
+# recommended_image="gr-combo-adsb-lora_sdr:latest"
+# )
+```
+
+---
+
+## `install_oot_module`
+
+Install an OOT module into a Docker image.
+
+Clones the git repo, compiles with cmake, and creates a reusable Docker image.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `git_url` | `str` | - | Git repository URL |
+| `branch` | `str` | `"main"` | Git branch to build from |
+| `build_deps` | `list[str] \| None` | `None` | Extra apt packages for compilation |
+| `cmake_args` | `list[str] \| None` | `None` | Extra cmake flags |
+| `base_image` | `str \| None` | `None` | Base image (default: gnuradio-runtime:latest) |
+| `force` | `bool` | `False` | Rebuild even if image exists |
+
+### Returns
+
+**Type:** `OOTInstallResult`
+
+Installation result with image tag.
+
+### Example
+
+```python
+# Basic install
+result = install_oot_module(
+ git_url="https://github.com/tapparelj/gr-lora_sdr",
+ branch="master"
+)
+# Returns: OOTInstallResult(
+# success=True,
+# module_name="lora_sdr",
+# image_tag="gnuradio-lora_sdr-runtime:latest",
+# error=None
+# )
+
+# With build dependencies
+result = install_oot_module(
+ git_url="https://github.com/hboeglen/gr-dab",
+ branch="maint-3.10",
+ build_deps=["autoconf", "automake", "libtool", "libfaad-dev"],
+ cmake_args=["-DENABLE_DOXYGEN=OFF"]
+)
+```
+
+---
+
+## `list_oot_images`
+
+List all installed OOT module images.
+
+### Returns
+
+**Type:** `list[OOTImageInfo]`
+
+List of installed images.
+
+### Example
+
+```python
+images = list_oot_images()
+# Returns: [
+# OOTImageInfo(
+# module_name="lora_sdr",
+# image_tag="gnuradio-lora_sdr-runtime:latest",
+# git_url="https://github.com/tapparelj/gr-lora_sdr",
+# branch="master"
+# ),
+# ...
+# ]
+```
+
+---
+
+## `remove_oot_image`
+
+Remove an OOT module image and its registry entry.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `module_name` | `str` | - | Module name to remove |
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+remove_oot_image(module_name="lora_sdr")
+# Returns: True
+```
+
+---
+
+## `build_multi_oot_image`
+
+Combine multiple OOT modules into a single Docker image.
+
+Missing modules that exist in the catalog are auto-built first.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `module_names` | `list[str]` | - | Module names to combine |
+| `force` | `bool` | `False` | Rebuild even if exists |
+
+### Returns
+
+**Type:** `ComboImageResult`
+
+Combo image details.
+
+### Example
+
+```python
+result = build_multi_oot_image(module_names=["lora_sdr", "adsb"])
+# Returns: ComboImageResult(
+# success=True,
+# image=ComboImageInfo(
+# combo_key="combo:adsb+lora_sdr",
+# image_tag="gr-combo-adsb-lora_sdr:latest",
+# modules=["adsb", "lora_sdr"]
+# ),
+# error=None
+# )
+```
+
+---
+
+## `list_combo_images`
+
+List all combined multi-OOT images.
+
+### Returns
+
+**Type:** `list[ComboImageInfo]`
+
+List of combo images.
+
+### Example
+
+```python
+combos = list_combo_images()
+# Returns: [
+# ComboImageInfo(
+# combo_key="combo:adsb+lora_sdr",
+# image_tag="gr-combo-adsb-lora_sdr:latest",
+# modules=["adsb", "lora_sdr"]
+# ),
+# ...
+# ]
+```
+
+---
+
+## `remove_combo_image`
+
+Remove a combined image by its combo key.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `combo_key` | `str` | - | Combo key (e.g., "combo:adsb+lora_sdr") |
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+remove_combo_image(combo_key="combo:adsb+lora_sdr")
+# Returns: True
+```
diff --git a/docs/src/content/docs/reference/tools/platform.mdx b/docs/src/content/docs/reference/tools/platform.mdx
new file mode 100644
index 0000000..4343894
--- /dev/null
+++ b/docs/src/content/docs/reference/tools/platform.mdx
@@ -0,0 +1,173 @@
+---
+title: Platform Tools
+description: Tools for discovering blocks and managing paths
+draft: false
+---
+
+Tools for discovering available blocks and managing OOT block paths.
+
+## `get_all_available_blocks`
+
+List all block types available on the platform.
+
+### Returns
+
+**Type:** `list[BlockTypeModel]`
+
+List of block types with key and category.
+
+### Example
+
+```python
+blocks = get_all_available_blocks()
+# Returns: [
+# BlockTypeModel(key="osmosdr_source", category="OsmoSDR"),
+# BlockTypeModel(key="analog_sig_source_x", category="Waveform Generators"),
+# ...
+# ]
+```
+
+---
+
+## `search_blocks`
+
+Search available blocks by keyword and/or category.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `query` | `str` | `""` | Search keyword |
+| `category` | `str \| None` | `None` | Filter by category |
+
+### Returns
+
+**Type:** `list[BlockTypeDetailModel]`
+
+Matching blocks with full details.
+
+### Example
+
+```python
+# Search by keyword
+blocks = search_blocks(query="filter")
+# Returns: [BlockTypeDetailModel(key="low_pass_filter", ...), ...]
+
+# Search by category
+blocks = search_blocks(category="Audio")
+# Returns: [BlockTypeDetailModel(key="audio_sink", ...), ...]
+
+# Combined search
+blocks = search_blocks(query="sink", category="Audio")
+```
+
+---
+
+## `get_block_categories`
+
+Get all block categories with their block keys.
+
+### Returns
+
+**Type:** `dict[str, list[str]]`
+
+Dict mapping category name to list of block keys.
+
+### Example
+
+```python
+categories = get_block_categories()
+# Returns: {
+# "Audio": ["audio_sink", "audio_source"],
+# "OsmoSDR": ["osmosdr_source", "osmosdr_sink"],
+# "Filters": ["low_pass_filter", "high_pass_filter", ...],
+# ...
+# }
+```
+
+---
+
+## `load_oot_blocks`
+
+Load OOT (Out-of-Tree) block paths into the platform.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `paths` | `list[str]` | - | Directories containing `.block.yml` files |
+
+### Returns
+
+**Type:** `dict`
+
+Result with added_paths, invalid_paths, and block counts.
+
+### Example
+
+```python
+result = load_oot_blocks(paths=[
+ "/usr/local/share/gnuradio/grc/blocks",
+ "/home/user/gr-modules/lib/grc"
+])
+# Returns: {
+# "added_paths": ["/usr/local/share/gnuradio/grc/blocks"],
+# "invalid_paths": ["/home/user/gr-modules/lib/grc"],
+# "blocks_before": 200,
+# "blocks_after": 215
+# }
+```
+
+---
+
+## `add_block_path`
+
+Add a single directory containing OOT module block YAML files.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `path` | `str` | - | Directory path |
+
+### Returns
+
+**Type:** `BlockPathsModel`
+
+Updated paths and block count.
+
+### Example
+
+```python
+result = add_block_path(path="/usr/local/share/gnuradio/grc/blocks")
+# Returns: BlockPathsModel(
+# paths=[...],
+# total_blocks=215,
+# blocks_added=15
+# )
+```
+
+---
+
+## `get_block_paths`
+
+Show current OOT block paths and total block count.
+
+### Returns
+
+**Type:** `BlockPathsModel`
+
+Current paths configuration.
+
+### Example
+
+```python
+paths = get_block_paths()
+# Returns: BlockPathsModel(
+# paths=[
+# "/usr/share/gnuradio/grc/blocks",
+# "/usr/local/share/gnuradio/grc/blocks"
+# ],
+# total_blocks=215
+# )
+```
diff --git a/docs/src/content/docs/reference/tools/runtime-mode.mdx b/docs/src/content/docs/reference/tools/runtime-mode.mdx
new file mode 100644
index 0000000..76b07c6
--- /dev/null
+++ b/docs/src/content/docs/reference/tools/runtime-mode.mdx
@@ -0,0 +1,170 @@
+---
+title: Runtime Mode
+description: Tools for enabling runtime features and checking capabilities
+draft: false
+---
+
+import { Aside } from '@astrojs/starlight/components';
+
+Tools for managing runtime mode and inspecting MCP client capabilities.
+
+
+
+## `get_runtime_mode`
+
+Check if runtime mode is enabled and what capabilities are available.
+
+### Returns
+
+**Type:** `RuntimeModeStatus`
+
+Status including enabled state, registered tools, and availability.
+
+### Example
+
+```python
+status = get_runtime_mode()
+# Returns: RuntimeModeStatus(
+# enabled=False,
+# tools_registered=[],
+# docker_available=True,
+# oot_available=True
+# )
+```
+
+---
+
+## `enable_runtime_mode`
+
+Enable runtime mode, registering all runtime control tools.
+
+This adds tools for:
+- XML-RPC connection and variable control
+- ControlPort/Thrift for performance monitoring
+- Docker container lifecycle (if Docker available)
+- OOT module installation (if Docker available)
+
+### Returns
+
+**Type:** `RuntimeModeStatus`
+
+Updated status with newly registered tools.
+
+### Example
+
+```python
+status = enable_runtime_mode()
+# Returns: RuntimeModeStatus(
+# enabled=True,
+# tools_registered=[
+# "launch_flowgraph", "list_containers", "stop_flowgraph",
+# "connect", "list_variables", "get_variable", "set_variable",
+# ...
+# ],
+# docker_available=True,
+# oot_available=True
+# )
+```
+
+### Notes
+
+- Calling when already enabled is a no-op (returns current status)
+- If Docker is unavailable, container-related tools are not registered
+- If Docker is unavailable, OOT tools are not registered
+
+---
+
+## `disable_runtime_mode`
+
+Disable runtime mode, removing runtime tools to reduce context.
+
+Use this when you're done with runtime operations and want to reduce the tool list
+for flowgraph design work.
+
+### Returns
+
+**Type:** `RuntimeModeStatus`
+
+Updated status showing disabled state.
+
+### Example
+
+```python
+status = disable_runtime_mode()
+# Returns: RuntimeModeStatus(
+# enabled=False,
+# tools_registered=[],
+# docker_available=True,
+# oot_available=True
+# )
+```
+
+---
+
+## `get_client_capabilities`
+
+Get the connected MCP client's capabilities.
+
+Useful for debugging MCP connections and understanding what features the client supports.
+
+### Returns
+
+**Type:** `ClientCapabilities`
+
+Client information and capability flags.
+
+### Example
+
+```python
+caps = get_client_capabilities()
+# Returns: ClientCapabilities(
+# client_name="claude-code",
+# client_version="2.1.15",
+# protocol_version="2024-11-05",
+# roots=RootsCapability(supported=True, list_changed=True),
+# sampling=SamplingCapability(supported=True, tools=True, context=False),
+# elicitation=ElicitationCapability(supported=False, form=False, url=False),
+# raw_capabilities={"roots": {"listChanged": True}, ...},
+# experimental={}
+# )
+```
+
+### Capability Explanations
+
+| Capability | Description |
+|------------|-------------|
+| `roots` | Client exposes workspace directories |
+| `sampling` | Server can request LLM completions |
+| `elicitation` | Server can prompt user for input |
+
+---
+
+## `list_client_roots`
+
+List the root directories advertised by the MCP client.
+
+Roots represent project directories or workspaces the client wants the server to be aware of.
+
+### Returns
+
+**Type:** `list[ClientRoot]`
+
+List of root directories.
+
+### Example
+
+```python
+roots = list_client_roots()
+# Returns: [
+# ClientRoot(uri="file:///home/user/project", name="project"),
+# ClientRoot(uri="file:///home/user/gr-mcp", name="gr-mcp"),
+# ]
+```
+
+### Notes
+
+- Returns empty list if roots capability is not supported
+- Typically includes the current working directory
diff --git a/docs/src/content/docs/reference/tools/validation.mdx b/docs/src/content/docs/reference/tools/validation.mdx
new file mode 100644
index 0000000..1d76685
--- /dev/null
+++ b/docs/src/content/docs/reference/tools/validation.mdx
@@ -0,0 +1,98 @@
+---
+title: Validation Tools
+description: Tools for checking flowgraph validity
+draft: false
+---
+
+Tools for validating blocks and flowgraphs.
+
+## `validate_block`
+
+Validate a single block's configuration.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `block_name` | `str` | - | Name of the block to validate |
+
+### Returns
+
+**Type:** `bool`
+
+True if the block is valid.
+
+### Example
+
+```python
+is_valid = validate_block(block_name="osmosdr_source_0")
+# Returns: True
+```
+
+---
+
+## `validate_flowgraph`
+
+Validate the entire flowgraph.
+
+Checks all blocks and connections for errors.
+
+### Returns
+
+**Type:** `bool`
+
+True if the flowgraph is valid.
+
+### Example
+
+```python
+is_valid = validate_flowgraph()
+# Returns: True
+```
+
+### Notes
+
+- Validation checks parameter types, required connections, and compatibility
+- Use `get_all_errors()` to see specific validation failures
+- `generate_code()` does not require validation to pass
+
+---
+
+## `get_all_errors`
+
+Get all validation errors from the flowgraph.
+
+### Returns
+
+**Type:** `list[ErrorModel]`
+
+List of errors with block name, parameter, and message.
+
+### Example
+
+```python
+errors = get_all_errors()
+# Returns: [
+# ErrorModel(
+# block="audio_sink_0",
+# param="samp_rate",
+# message="Sample rate 50000 not supported by audio device"
+# ),
+# ...
+# ]
+
+# Empty list means no errors
+errors = get_all_errors()
+# Returns: []
+```
+
+### Error Types
+
+Common validation errors:
+
+| Error Type | Example |
+|------------|---------|
+| Missing connection | "Block has unconnected input port" |
+| Type mismatch | "Cannot connect complex to float" |
+| Invalid parameter | "Invalid sample rate value" |
+| Missing dependency | "Block requires gr-osmosdr" |
diff --git a/docs/src/content/docs/reference/tools/xmlrpc.mdx b/docs/src/content/docs/reference/tools/xmlrpc.mdx
new file mode 100644
index 0000000..fa8ea0d
--- /dev/null
+++ b/docs/src/content/docs/reference/tools/xmlrpc.mdx
@@ -0,0 +1,263 @@
+---
+title: XML-RPC Tools
+description: Tools for XML-RPC connection and variable control
+draft: false
+---
+
+import { Aside } from '@astrojs/starlight/components';
+
+Tools for connecting to running flowgraphs via XML-RPC and controlling variables.
+
+
+
+## `connect`
+
+Connect to a GNU Radio XML-RPC endpoint by URL.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `url` | `str` | - | XML-RPC URL (e.g., `http://localhost:8080`) |
+
+### Returns
+
+**Type:** `ConnectionInfoModel`
+
+Connection details including URL and port.
+
+### Example
+
+```python
+connect(url="http://localhost:8080")
+# Returns: ConnectionInfoModel(
+# url="http://localhost:8080",
+# xmlrpc_port=8080,
+# container_name=None
+# )
+```
+
+---
+
+## `connect_to_container`
+
+Connect to a flowgraph by container name (resolves port automatically).
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `name` | `str` | - | Container name |
+
+### Returns
+
+**Type:** `ConnectionInfoModel`
+
+Connection details.
+
+### Example
+
+```python
+connect_to_container(name="fm-radio")
+# Returns: ConnectionInfoModel(
+# url="http://localhost:32768",
+# xmlrpc_port=32768,
+# container_name="fm-radio"
+# )
+```
+
+---
+
+## `disconnect`
+
+Disconnect from the current XML-RPC (and ControlPort) endpoint.
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+disconnect()
+# Returns: True
+```
+
+---
+
+## `get_status`
+
+Get runtime status including connection and container info.
+
+### Returns
+
+**Type:** `RuntimeStatusModel`
+
+Current runtime state.
+
+### Example
+
+```python
+status = get_status()
+# Returns: RuntimeStatusModel(
+# connected=True,
+# connection=ConnectionInfoModel(...),
+# containers=[ContainerModel(...), ...]
+# )
+```
+
+---
+
+## `list_variables`
+
+List all XML-RPC-exposed variables.
+
+### Returns
+
+**Type:** `list[VariableModel]`
+
+List of variables with name, value, and type.
+
+### Example
+
+```python
+vars = list_variables()
+# Returns: [
+# VariableModel(name="freq", value=101100000.0, type="float"),
+# VariableModel(name="gain", value=40, type="int"),
+# VariableModel(name="samp_rate", value=2000000.0, type="float"),
+# ]
+```
+
+---
+
+## `get_variable`
+
+Get a variable value.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `name` | `str` | - | Variable name |
+
+### Returns
+
+**Type:** `Any`
+
+Current variable value.
+
+### Example
+
+```python
+freq = get_variable(name="freq")
+# Returns: 101100000.0
+```
+
+---
+
+## `set_variable`
+
+Set a variable value.
+
+### Parameters
+
+| Name | Type | Default | Description |
+|------|------|---------|-------------|
+| `name` | `str` | - | Variable name |
+| `value` | `Any` | - | New value |
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+set_variable(name="freq", value=98.5e6)
+# Returns: True
+```
+
+---
+
+## `start`
+
+Start the connected flowgraph.
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+start()
+# Returns: True
+```
+
+---
+
+## `stop`
+
+Stop the connected flowgraph.
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+stop()
+# Returns: True
+```
+
+---
+
+## `lock`
+
+Lock the flowgraph for thread-safe parameter updates.
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+lock()
+set_variable(name="freq", value=102.7e6)
+set_variable(name="gain", value=35)
+unlock()
+```
+
+---
+
+## `unlock`
+
+Unlock the flowgraph after parameter updates.
+
+### Returns
+
+**Type:** `bool`
+
+True if successful.
+
+### Example
+
+```python
+unlock()
+# Returns: True
+```
diff --git a/docs/src/styles/custom.css b/docs/src/styles/custom.css
new file mode 100644
index 0000000..eb230fa
--- /dev/null
+++ b/docs/src/styles/custom.css
@@ -0,0 +1,63 @@
+/* GR-MCP Documentation Theme */
+
+:root {
+ /* Brand colors - no purple, clean tech aesthetic */
+ --sl-color-accent-low: #1e3a5f;
+ --sl-color-accent: #0ea5e9;
+ --sl-color-accent-high: #7dd3fc;
+
+ /* Text colors */
+ --sl-color-white: #f8fafc;
+ --sl-color-gray-1: #e2e8f0;
+ --sl-color-gray-2: #cbd5e1;
+ --sl-color-gray-3: #94a3b8;
+ --sl-color-gray-4: #64748b;
+ --sl-color-gray-5: #475569;
+ --sl-color-gray-6: #1e293b;
+ --sl-color-black: #0f172a;
+}
+
+/* Dark mode refinements */
+:root[data-theme='dark'] {
+ --sl-color-bg: #0f172a;
+ --sl-color-bg-nav: #1e293b;
+ --sl-color-bg-sidebar: #1e293b;
+}
+
+/* Code blocks - terminal aesthetic */
+.expressive-code {
+ --ec-brdRad: 6px;
+}
+
+/* Headings - clean and readable */
+h1, h2, h3, h4 {
+ font-weight: 600;
+ letter-spacing: -0.01em;
+}
+
+/* Links in prose */
+.sl-markdown-content a:not(.card-grid a) {
+ text-decoration-thickness: 1px;
+ text-underline-offset: 2px;
+}
+
+/* Tables - better contrast */
+table {
+ width: 100%;
+}
+
+th {
+ background: var(--sl-color-gray-6);
+ font-weight: 600;
+}
+
+/* Callouts/asides - consistent with brand */
+.starlight-aside--tip {
+ --sl-color-asides-text-accent: var(--sl-color-accent);
+ border-color: var(--sl-color-accent);
+}
+
+/* Hero section tweaks */
+.hero {
+ padding-block: 3rem;
+}
diff --git a/examples/combo_test_adsb_lora.py b/examples/combo_test_adsb_lora.py
new file mode 100644
index 0000000..d132cd9
--- /dev/null
+++ b/examples/combo_test_adsb_lora.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python3
+"""Minimal flowgraph that uses blocks from both adsb and lora_sdr.
+
+Verifies combo Docker images contain working blocks from multiple
+OOT modules. Runs for 5 seconds then exits cleanly.
+"""
+
+import signal
+import sys
+import time
+from xmlrpc.server import SimpleXMLRPCServer
+import threading
+
+from gnuradio import gr, blocks, analog
+
+# Import OOT modules to verify they're available
+from gnuradio import adsb as gr_adsb
+from gnuradio import lora_sdr as gr_lora_sdr
+
+
+class combo_test(gr.top_block):
+ def __init__(self):
+ gr.top_block.__init__(self, "Combo Test: ADS-B + LoRa SDR")
+
+ ##################################################
+ # Variables
+ ##################################################
+ self.samp_rate = samp_rate = 2e6
+
+ ##################################################
+ # ADS-B: noise(float) -> throttle -> demod -> null
+ ##################################################
+ self.noise_adsb = analog.noise_source_f(analog.GR_GAUSSIAN, 0.01, 0)
+ self.throttle_adsb = blocks.throttle(gr.sizeof_float, samp_rate, True)
+ self.adsb_demod = gr_adsb.demod(samp_rate)
+ self.null_adsb = blocks.null_sink(gr.sizeof_float)
+
+ self.connect(self.noise_adsb, self.throttle_adsb, self.adsb_demod, self.null_adsb)
+
+ ##################################################
+ # LoRa: tx -> throttle -> null
+ ##################################################
+ self.lora_tx = gr_lora_sdr.lora_sdr_lora_tx(
+ samp_rate=125000,
+ bw=125000,
+ sf=7,
+ impl_head=True,
+ cr=1,
+ has_crc=True,
+ ldro_mode=2,
+ frame_zero_padd=128,
+ )
+ self.throttle_lora = blocks.throttle(gr.sizeof_gr_complex, 125000, True)
+ self.null_lora = blocks.null_sink(gr.sizeof_gr_complex)
+
+ self.connect(self.lora_tx, self.throttle_lora, self.null_lora)
+
+ ##################################################
+ # XML-RPC for runtime control
+ ##################################################
+ self.xmlrpc_port = 8080
+ self.xmlrpc_server = SimpleXMLRPCServer(
+ ("0.0.0.0", self.xmlrpc_port),
+ allow_none=True,
+ logRequests=False,
+ )
+ self.xmlrpc_server.register_instance(self)
+ threading.Thread(
+ target=self.xmlrpc_server.serve_forever,
+ daemon=True,
+ ).start()
+
+
+def main():
+ tb = combo_test()
+ print(f"[combo_test] Starting flowgraph with ADS-B + LoRa SDR blocks")
+ print(f"[combo_test] XML-RPC on port {tb.xmlrpc_port}")
+ print(f"[combo_test] ADS-B demod: {type(tb.adsb_demod).__name__}")
+ print(f"[combo_test] LoRa TX: {type(tb.lora_tx).__name__}")
+ sys.stdout.flush()
+ tb.start()
+
+ def sig_handler(sig, frame):
+ tb.stop()
+ tb.wait()
+ sys.exit(0)
+
+ signal.signal(signal.SIGTERM, sig_handler)
+ signal.signal(signal.SIGINT, sig_handler)
+
+ # Run for a bit then exit
+ time.sleep(5)
+ print("[combo_test] Flowgraph ran successfully for 5s, stopping")
+ sys.stdout.flush()
+ tb.stop()
+ tb.wait()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/lora_channel_scanner.grc b/examples/lora_channel_scanner.grc
new file mode 100644
index 0000000..83ad5e6
--- /dev/null
+++ b/examples/lora_channel_scanner.grc
@@ -0,0 +1,540 @@
+options:
+ parameters:
+ author: gr-mcp
+ catch_exceptions: 'True'
+ category: '[GRC Hier Blocks]'
+ cmake_opt: ''
+ comment: ''
+ copyright: ''
+ description: Scans US ISM 915MHz band for LoRa activity. Retune via XML-RPC.
+ gen_cmake: 'On'
+ gen_linking: dynamic
+ generate_options: no_gui
+ hier_block_src_path: '.:'
+ id: lora_channel_scanner
+ max_nouts: '0'
+ output_language: python
+ placement: (0,0)
+ qt_qss_theme: ''
+ realtime_scheduling: ''
+ run: 'True'
+ run_command: '{python} -u {filename}'
+ run_options: prompt
+ sizing_mode: fixed
+ thread_safe_setters: ''
+ title: LoRa Channel Scanner
+ window_size: (1000,1000)
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [8, 8]
+ rotation: 0
+ state: enabled
+
+blocks:
+- name: center_freq
+ id: variable
+ parameters:
+ comment: ''
+ value: 915e6
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: channel_index
+ id: variable
+ parameters:
+ comment: ''
+ value: '0'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: lora_bw
+ id: variable
+ parameters:
+ comment: ''
+ value: '125000'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: lora_cr
+ id: variable
+ parameters:
+ comment: ''
+ value: '1'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: lora_sf
+ id: variable
+ parameters:
+ comment: ''
+ value: '7'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: rf_gain
+ id: variable
+ parameters:
+ comment: ''
+ value: '40'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: samp_rate
+ id: variable
+ parameters:
+ comment: ''
+ value: '1000000'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: blocks_message_debug_0
+ id: blocks_message_debug
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ en_uvec: 'True'
+ log_level: info
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: lora_rx_0
+ id: lora_rx
+ parameters:
+ affinity: ''
+ alias: ''
+ bw: lora_bw
+ comment: ''
+ cr: lora_cr
+ has_crc: 'True'
+ impl_head: 'False'
+ ldro: '2'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ pay_len: '255'
+ print_rx: '[True,True]'
+ samp_rate: int(samp_rate/2)
+ sf: lora_sf
+ soft_decoding: 'True'
+ sync_word: '[0x12]'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: low_pass_filter_0
+ id: low_pass_filter
+ parameters:
+ affinity: ''
+ alias: ''
+ beta: '6.76'
+ comment: ''
+ cutoff_freq: 200e3
+ decim: '2'
+ gain: '1'
+ interp: '1'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ samp_rate: samp_rate
+ type: fir_filter_ccf
+ width: 50e3
+ win: window.WIN_HAMMING
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: osmosdr_source_0
+ id: osmosdr_source
+ parameters:
+ affinity: ''
+ alias: ''
+ ant0: ''
+ ant1: ''
+ ant10: ''
+ ant11: ''
+ ant12: ''
+ ant13: ''
+ ant14: ''
+ ant15: ''
+ ant16: ''
+ ant17: ''
+ ant18: ''
+ ant19: ''
+ ant2: ''
+ ant20: ''
+ ant21: ''
+ ant22: ''
+ ant23: ''
+ ant24: ''
+ ant25: ''
+ ant26: ''
+ ant27: ''
+ ant28: ''
+ ant29: ''
+ ant3: ''
+ ant30: ''
+ ant31: ''
+ ant4: ''
+ ant5: ''
+ ant6: ''
+ ant7: ''
+ ant8: ''
+ ant9: ''
+ args: rtl=0
+ bb_gain0: '20'
+ bb_gain1: '20'
+ bb_gain10: '20'
+ bb_gain11: '20'
+ bb_gain12: '20'
+ bb_gain13: '20'
+ bb_gain14: '20'
+ bb_gain15: '20'
+ bb_gain16: '20'
+ bb_gain17: '20'
+ bb_gain18: '20'
+ bb_gain19: '20'
+ bb_gain2: '20'
+ bb_gain20: '20'
+ bb_gain21: '20'
+ bb_gain22: '20'
+ bb_gain23: '20'
+ bb_gain24: '20'
+ bb_gain25: '20'
+ bb_gain26: '20'
+ bb_gain27: '20'
+ bb_gain28: '20'
+ bb_gain29: '20'
+ bb_gain3: '20'
+ bb_gain30: '20'
+ bb_gain31: '20'
+ bb_gain4: '20'
+ bb_gain5: '20'
+ bb_gain6: '20'
+ bb_gain7: '20'
+ bb_gain8: '20'
+ bb_gain9: '20'
+ bw0: '0'
+ bw1: '0'
+ bw10: '0'
+ bw11: '0'
+ bw12: '0'
+ bw13: '0'
+ bw14: '0'
+ bw15: '0'
+ bw16: '0'
+ bw17: '0'
+ bw18: '0'
+ bw19: '0'
+ bw2: '0'
+ bw20: '0'
+ bw21: '0'
+ bw22: '0'
+ bw23: '0'
+ bw24: '0'
+ bw25: '0'
+ bw26: '0'
+ bw27: '0'
+ bw28: '0'
+ bw29: '0'
+ bw3: '0'
+ bw30: '0'
+ bw31: '0'
+ bw4: '0'
+ bw5: '0'
+ bw6: '0'
+ bw7: '0'
+ bw8: '0'
+ bw9: '0'
+ clock_source0: ''
+ clock_source1: ''
+ clock_source2: ''
+ clock_source3: ''
+ clock_source4: ''
+ clock_source5: ''
+ clock_source6: ''
+ clock_source7: ''
+ comment: ''
+ corr0: '0'
+ corr1: '0'
+ corr10: '0'
+ corr11: '0'
+ corr12: '0'
+ corr13: '0'
+ corr14: '0'
+ corr15: '0'
+ corr16: '0'
+ corr17: '0'
+ corr18: '0'
+ corr19: '0'
+ corr2: '0'
+ corr20: '0'
+ corr21: '0'
+ corr22: '0'
+ corr23: '0'
+ corr24: '0'
+ corr25: '0'
+ corr26: '0'
+ corr27: '0'
+ corr28: '0'
+ corr29: '0'
+ corr3: '0'
+ corr30: '0'
+ corr31: '0'
+ corr4: '0'
+ corr5: '0'
+ corr6: '0'
+ corr7: '0'
+ corr8: '0'
+ corr9: '0'
+ dc_offset_mode0: '2'
+ dc_offset_mode1: '0'
+ dc_offset_mode10: '0'
+ dc_offset_mode11: '0'
+ dc_offset_mode12: '0'
+ dc_offset_mode13: '0'
+ dc_offset_mode14: '0'
+ dc_offset_mode15: '0'
+ dc_offset_mode16: '0'
+ dc_offset_mode17: '0'
+ dc_offset_mode18: '0'
+ dc_offset_mode19: '0'
+ dc_offset_mode2: '0'
+ dc_offset_mode20: '0'
+ dc_offset_mode21: '0'
+ dc_offset_mode22: '0'
+ dc_offset_mode23: '0'
+ dc_offset_mode24: '0'
+ dc_offset_mode25: '0'
+ dc_offset_mode26: '0'
+ dc_offset_mode27: '0'
+ dc_offset_mode28: '0'
+ dc_offset_mode29: '0'
+ dc_offset_mode3: '0'
+ dc_offset_mode30: '0'
+ dc_offset_mode31: '0'
+ dc_offset_mode4: '0'
+ dc_offset_mode5: '0'
+ dc_offset_mode6: '0'
+ dc_offset_mode7: '0'
+ dc_offset_mode8: '0'
+ dc_offset_mode9: '0'
+ freq0: center_freq
+ freq1: 100e6
+ freq10: 100e6
+ freq11: 100e6
+ freq12: 100e6
+ freq13: 100e6
+ freq14: 100e6
+ freq15: 100e6
+ freq16: 100e6
+ freq17: 100e6
+ freq18: 100e6
+ freq19: 100e6
+ freq2: 100e6
+ freq20: 100e6
+ freq21: 100e6
+ freq22: 100e6
+ freq23: 100e6
+ freq24: 100e6
+ freq25: 100e6
+ freq26: 100e6
+ freq27: 100e6
+ freq28: 100e6
+ freq29: 100e6
+ freq3: 100e6
+ freq30: 100e6
+ freq31: 100e6
+ freq4: 100e6
+ freq5: 100e6
+ freq6: 100e6
+ freq7: 100e6
+ freq8: 100e6
+ freq9: 100e6
+ gain0: rf_gain
+ gain1: '10'
+ gain10: '10'
+ gain11: '10'
+ gain12: '10'
+ gain13: '10'
+ gain14: '10'
+ gain15: '10'
+ gain16: '10'
+ gain17: '10'
+ gain18: '10'
+ gain19: '10'
+ gain2: '10'
+ gain20: '10'
+ gain21: '10'
+ gain22: '10'
+ gain23: '10'
+ gain24: '10'
+ gain25: '10'
+ gain26: '10'
+ gain27: '10'
+ gain28: '10'
+ gain29: '10'
+ gain3: '10'
+ gain30: '10'
+ gain31: '10'
+ gain4: '10'
+ gain5: '10'
+ gain6: '10'
+ gain7: '10'
+ gain8: '10'
+ gain9: '10'
+ gain_mode0: 'False'
+ gain_mode1: 'False'
+ gain_mode10: 'False'
+ gain_mode11: 'False'
+ gain_mode12: 'False'
+ gain_mode13: 'False'
+ gain_mode14: 'False'
+ gain_mode15: 'False'
+ gain_mode16: 'False'
+ gain_mode17: 'False'
+ gain_mode18: 'False'
+ gain_mode19: 'False'
+ gain_mode2: 'False'
+ gain_mode20: 'False'
+ gain_mode21: 'False'
+ gain_mode22: 'False'
+ gain_mode23: 'False'
+ gain_mode24: 'False'
+ gain_mode25: 'False'
+ gain_mode26: 'False'
+ gain_mode27: 'False'
+ gain_mode28: 'False'
+ gain_mode29: 'False'
+ gain_mode3: 'False'
+ gain_mode30: 'False'
+ gain_mode31: 'False'
+ gain_mode4: 'False'
+ gain_mode5: 'False'
+ gain_mode6: 'False'
+ gain_mode7: 'False'
+ gain_mode8: 'False'
+ gain_mode9: 'False'
+ if_gain0: '20'
+ if_gain1: '20'
+ if_gain10: '20'
+ if_gain11: '20'
+ if_gain12: '20'
+ if_gain13: '20'
+ if_gain14: '20'
+ if_gain15: '20'
+ if_gain16: '20'
+ if_gain17: '20'
+ if_gain18: '20'
+ if_gain19: '20'
+ if_gain2: '20'
+ if_gain20: '20'
+ if_gain21: '20'
+ if_gain22: '20'
+ if_gain23: '20'
+ if_gain24: '20'
+ if_gain25: '20'
+ if_gain26: '20'
+ if_gain27: '20'
+ if_gain28: '20'
+ if_gain29: '20'
+ if_gain3: '20'
+ if_gain30: '20'
+ if_gain31: '20'
+ if_gain4: '20'
+ if_gain5: '20'
+ if_gain6: '20'
+ if_gain7: '20'
+ if_gain8: '20'
+ if_gain9: '20'
+ iq_balance_mode0: '2'
+ iq_balance_mode1: '0'
+ iq_balance_mode10: '0'
+ iq_balance_mode11: '0'
+ iq_balance_mode12: '0'
+ iq_balance_mode13: '0'
+ iq_balance_mode14: '0'
+ iq_balance_mode15: '0'
+ iq_balance_mode16: '0'
+ iq_balance_mode17: '0'
+ iq_balance_mode18: '0'
+ iq_balance_mode19: '0'
+ iq_balance_mode2: '0'
+ iq_balance_mode20: '0'
+ iq_balance_mode21: '0'
+ iq_balance_mode22: '0'
+ iq_balance_mode23: '0'
+ iq_balance_mode24: '0'
+ iq_balance_mode25: '0'
+ iq_balance_mode26: '0'
+ iq_balance_mode27: '0'
+ iq_balance_mode28: '0'
+ iq_balance_mode29: '0'
+ iq_balance_mode3: '0'
+ iq_balance_mode30: '0'
+ iq_balance_mode31: '0'
+ iq_balance_mode4: '0'
+ iq_balance_mode5: '0'
+ iq_balance_mode6: '0'
+ iq_balance_mode7: '0'
+ iq_balance_mode8: '0'
+ iq_balance_mode9: '0'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ nchan: '1'
+ num_mboards: '1'
+ sample_rate: samp_rate
+ sync: sync
+ time_source0: ''
+ time_source1: ''
+ time_source2: ''
+ time_source3: ''
+ time_source4: ''
+ time_source5: ''
+ time_source6: ''
+ time_source7: ''
+ type: fc32
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: xmlrpc_server_0
+ id: xmlrpc_server
+ parameters:
+ addr: localhost
+ alias: ''
+ comment: ''
+ port: '8080'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+
+connections:
+- [lora_rx_0, out, blocks_message_debug_0, print]
+- [low_pass_filter_0, '0', lora_rx_0, '0']
+- [osmosdr_source_0, '0', low_pass_filter_0, '0']
+
+metadata:
+ file_format: 1
+ grc_version: 3.10.12.0
diff --git a/examples/lora_channel_scanner.py b/examples/lora_channel_scanner.py
new file mode 100755
index 0000000..c8ba1ae
--- /dev/null
+++ b/examples/lora_channel_scanner.py
@@ -0,0 +1,167 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+#
+# SPDX-License-Identifier: GPL-3.0
+#
+# GNU Radio Python Flow Graph
+# Title: LoRa Channel Scanner
+# Author: gr-mcp
+# Description: Scans US ISM 915MHz band for LoRa activity. Retune via XML-RPC.
+# GNU Radio version: 3.10.12.0
+
+from gnuradio import blocks, gr
+from gnuradio import filter
+from gnuradio.filter import firdes
+from gnuradio import gr
+from gnuradio.fft import window
+import sys
+import signal
+from argparse import ArgumentParser
+from gnuradio.eng_arg import eng_float, intx
+from gnuradio import eng_notation
+from xmlrpc.server import SimpleXMLRPCServer
+import threading
+import gnuradio.lora_sdr as lora_sdr
+import osmosdr
+import time
+
+
+
+
+class lora_channel_scanner(gr.top_block):
+
+ def __init__(self):
+ gr.top_block.__init__(self, "LoRa Channel Scanner", catch_exceptions=True)
+ self.flowgraph_started = threading.Event()
+
+ ##################################################
+ # Variables
+ ##################################################
+ self.samp_rate = samp_rate = 1000000
+ self.rf_gain = rf_gain = 40
+ self.lora_sf = lora_sf = 7
+ self.lora_cr = lora_cr = 1
+ self.lora_bw = lora_bw = 125000
+ self.channel_index = channel_index = 0
+ self.center_freq = center_freq = 915e6
+
+ ##################################################
+ # Blocks
+ ##################################################
+
+ self.xmlrpc_server_0 = SimpleXMLRPCServer(('localhost', 8080), allow_none=True)
+ self.xmlrpc_server_0.register_instance(self)
+ self.xmlrpc_server_0_thread = threading.Thread(target=self.xmlrpc_server_0.serve_forever)
+ self.xmlrpc_server_0_thread.daemon = True
+ self.xmlrpc_server_0_thread.start()
+ self.osmosdr_source_0 = osmosdr.source(
+ args="numchan=" + str(1) + " " + 'rtl=0'
+ )
+ self.osmosdr_source_0.set_time_unknown_pps(osmosdr.time_spec_t())
+ self.osmosdr_source_0.set_sample_rate(samp_rate)
+ self.osmosdr_source_0.set_center_freq(center_freq, 0)
+ self.osmosdr_source_0.set_freq_corr(0, 0)
+ self.osmosdr_source_0.set_dc_offset_mode(2, 0)
+ self.osmosdr_source_0.set_iq_balance_mode(2, 0)
+ self.osmosdr_source_0.set_gain_mode(False, 0)
+ self.osmosdr_source_0.set_gain(rf_gain, 0)
+ self.osmosdr_source_0.set_if_gain(20, 0)
+ self.osmosdr_source_0.set_bb_gain(20, 0)
+ self.osmosdr_source_0.set_antenna('', 0)
+ self.osmosdr_source_0.set_bandwidth(0, 0)
+ self.low_pass_filter_0 = filter.fir_filter_ccf(
+ 2,
+ firdes.low_pass(
+ 1,
+ samp_rate,
+ 200e3,
+ 50e3,
+ window.WIN_HAMMING,
+ 6.76))
+ self.lora_rx_0 = lora_sdr.lora_sdr_lora_rx( bw=lora_bw, cr=1, has_crc=True, impl_head=False, pay_len=255, samp_rate=(int(samp_rate/2)), sf=lora_sf, sync_word=[0x12], soft_decoding=True, ldro_mode=2, print_rx=[True,True])
+ self.blocks_message_debug_0 = blocks.message_debug(True, gr.log_levels.info)
+
+
+ ##################################################
+ # Connections
+ ##################################################
+ self.msg_connect((self.lora_rx_0, 'out'), (self.blocks_message_debug_0, 'print'))
+ self.connect((self.low_pass_filter_0, 0), (self.lora_rx_0, 0))
+ self.connect((self.osmosdr_source_0, 0), (self.low_pass_filter_0, 0))
+
+
+ def get_samp_rate(self):
+ return self.samp_rate
+
+ def set_samp_rate(self, samp_rate):
+ self.samp_rate = samp_rate
+ self.low_pass_filter_0.set_taps(firdes.low_pass(1, self.samp_rate, 200e3, 50e3, window.WIN_HAMMING, 6.76))
+ self.osmosdr_source_0.set_sample_rate(self.samp_rate)
+
+ def get_rf_gain(self):
+ return self.rf_gain
+
+ def set_rf_gain(self, rf_gain):
+ self.rf_gain = rf_gain
+ self.osmosdr_source_0.set_gain(self.rf_gain, 0)
+
+ def get_lora_sf(self):
+ return self.lora_sf
+
+ def set_lora_sf(self, lora_sf):
+ self.lora_sf = lora_sf
+
+ def get_lora_cr(self):
+ return self.lora_cr
+
+ def set_lora_cr(self, lora_cr):
+ self.lora_cr = lora_cr
+
+ def get_lora_bw(self):
+ return self.lora_bw
+
+ def set_lora_bw(self, lora_bw):
+ self.lora_bw = lora_bw
+
+ def get_channel_index(self):
+ return self.channel_index
+
+ def set_channel_index(self, channel_index):
+ self.channel_index = channel_index
+
+ def get_center_freq(self):
+ return self.center_freq
+
+ def set_center_freq(self, center_freq):
+ self.center_freq = center_freq
+ self.osmosdr_source_0.set_center_freq(self.center_freq, 0)
+
+
+
+
+def main(top_block_cls=lora_channel_scanner, options=None):
+ tb = top_block_cls()
+
+ def sig_handler(sig=None, frame=None):
+ tb.stop()
+ tb.wait()
+
+ sys.exit(0)
+
+ signal.signal(signal.SIGINT, sig_handler)
+ signal.signal(signal.SIGTERM, sig_handler)
+
+ tb.start()
+ tb.flowgraph_started.set()
+
+ try:
+ input('Press Enter to quit: ')
+ except EOFError:
+ pass
+ tb.stop()
+ tb.wait()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/lora_quality_analyzer.grc b/examples/lora_quality_analyzer.grc
new file mode 100644
index 0000000..b34650d
--- /dev/null
+++ b/examples/lora_quality_analyzer.grc
@@ -0,0 +1,673 @@
+options:
+ parameters:
+ author: gr-mcp
+ catch_exceptions: 'True'
+ category: '[GRC Hier Blocks]'
+ cmake_opt: ''
+ comment: ''
+ copyright: ''
+ description: LoRa decoder with IQ recording and real-time signal power measurement.
+ gen_cmake: 'On'
+ gen_linking: dynamic
+ generate_options: no_gui
+ hier_block_src_path: '.:'
+ id: lora_quality_analyzer
+ max_nouts: '0'
+ output_language: python
+ placement: (0,0)
+ qt_qss_theme: ''
+ realtime_scheduling: ''
+ run: 'True'
+ run_command: '{python} -u {filename}'
+ run_options: prompt
+ sizing_mode: fixed
+ thread_safe_setters: ''
+ title: LoRa Signal Quality Analyzer
+ window_size: (1000,1000)
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [8, 8]
+ rotation: 0
+ state: enabled
+
+blocks:
+- name: center_freq
+ id: variable
+ parameters:
+ comment: ''
+ value: 915e6
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: iq_file
+ id: variable
+ parameters:
+ comment: ''
+ value: '"/tmp/iq_capture.cf32"'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: lora_bw
+ id: variable
+ parameters:
+ comment: ''
+ value: '125000'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: lora_sf
+ id: variable
+ parameters:
+ comment: ''
+ value: '7'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: recording_selector
+ id: variable
+ parameters:
+ comment: ''
+ value: '1'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: rf_gain
+ id: variable
+ parameters:
+ comment: ''
+ value: '40'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: samp_rate
+ id: variable
+ parameters:
+ comment: ''
+ value: '1000000'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: signal_power_db
+ id: variable_function_probe
+ parameters:
+ block_id: blocks_probe_signal_x_0
+ comment: ''
+ function_args: ''
+ function_name: level
+ poll_rate: '2'
+ value: '0'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: blocks_complex_to_mag_squared_0
+ id: blocks_complex_to_mag_squared
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ vlen: '1'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: blocks_file_sink_0
+ id: blocks_file_sink
+ parameters:
+ affinity: ''
+ alias: ''
+ append: 'False'
+ comment: ''
+ file: iq_file
+ type: complex
+ unbuffered: 'False'
+ vlen: '1'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: blocks_message_debug_0
+ id: blocks_message_debug
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ en_uvec: 'True'
+ log_level: info
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: blocks_moving_average_xx_0
+ id: blocks_moving_average_xx
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ length: '10000'
+ max_iter: '4000'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ scale: 1.0/10000
+ type: float
+ vlen: '1'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: blocks_nlog10_ff_0
+ id: blocks_nlog10_ff
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ k: '0'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ n: '10'
+ vlen: '1'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: blocks_null_sink_0
+ id: blocks_null_sink
+ parameters:
+ affinity: ''
+ alias: ''
+ bus_structure_sink: '[[0,],]'
+ comment: ''
+ num_inputs: '1'
+ type: complex
+ vlen: '1'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: blocks_probe_signal_x_0
+ id: blocks_probe_signal_x
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ type: float
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: blocks_selector_0
+ id: blocks_selector
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ enabled: 'True'
+ input_index: '0'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ num_inputs: '1'
+ num_outputs: '2'
+ output_index: recording_selector
+ showports: 'True'
+ type: complex
+ vlen: '1'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: lora_rx_0
+ id: lora_rx
+ parameters:
+ affinity: ''
+ alias: ''
+ bw: lora_bw
+ comment: ''
+ cr: '1'
+ has_crc: 'True'
+ impl_head: 'False'
+ ldro: '2'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ pay_len: '255'
+ print_rx: '[True,True]'
+ samp_rate: int(samp_rate/2)
+ sf: lora_sf
+ soft_decoding: 'True'
+ sync_word: '[0x12]'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: low_pass_filter_0
+ id: low_pass_filter
+ parameters:
+ affinity: ''
+ alias: ''
+ beta: '6.76'
+ comment: ''
+ cutoff_freq: 200e3
+ decim: '2'
+ gain: '1'
+ interp: '1'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ samp_rate: samp_rate
+ type: fir_filter_ccf
+ width: 50e3
+ win: window.WIN_HAMMING
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: osmosdr_source_0
+ id: osmosdr_source
+ parameters:
+ affinity: ''
+ alias: ''
+ ant0: ''
+ ant1: ''
+ ant10: ''
+ ant11: ''
+ ant12: ''
+ ant13: ''
+ ant14: ''
+ ant15: ''
+ ant16: ''
+ ant17: ''
+ ant18: ''
+ ant19: ''
+ ant2: ''
+ ant20: ''
+ ant21: ''
+ ant22: ''
+ ant23: ''
+ ant24: ''
+ ant25: ''
+ ant26: ''
+ ant27: ''
+ ant28: ''
+ ant29: ''
+ ant3: ''
+ ant30: ''
+ ant31: ''
+ ant4: ''
+ ant5: ''
+ ant6: ''
+ ant7: ''
+ ant8: ''
+ ant9: ''
+ args: rtl=0
+ bb_gain0: '20'
+ bb_gain1: '20'
+ bb_gain10: '20'
+ bb_gain11: '20'
+ bb_gain12: '20'
+ bb_gain13: '20'
+ bb_gain14: '20'
+ bb_gain15: '20'
+ bb_gain16: '20'
+ bb_gain17: '20'
+ bb_gain18: '20'
+ bb_gain19: '20'
+ bb_gain2: '20'
+ bb_gain20: '20'
+ bb_gain21: '20'
+ bb_gain22: '20'
+ bb_gain23: '20'
+ bb_gain24: '20'
+ bb_gain25: '20'
+ bb_gain26: '20'
+ bb_gain27: '20'
+ bb_gain28: '20'
+ bb_gain29: '20'
+ bb_gain3: '20'
+ bb_gain30: '20'
+ bb_gain31: '20'
+ bb_gain4: '20'
+ bb_gain5: '20'
+ bb_gain6: '20'
+ bb_gain7: '20'
+ bb_gain8: '20'
+ bb_gain9: '20'
+ bw0: '0'
+ bw1: '0'
+ bw10: '0'
+ bw11: '0'
+ bw12: '0'
+ bw13: '0'
+ bw14: '0'
+ bw15: '0'
+ bw16: '0'
+ bw17: '0'
+ bw18: '0'
+ bw19: '0'
+ bw2: '0'
+ bw20: '0'
+ bw21: '0'
+ bw22: '0'
+ bw23: '0'
+ bw24: '0'
+ bw25: '0'
+ bw26: '0'
+ bw27: '0'
+ bw28: '0'
+ bw29: '0'
+ bw3: '0'
+ bw30: '0'
+ bw31: '0'
+ bw4: '0'
+ bw5: '0'
+ bw6: '0'
+ bw7: '0'
+ bw8: '0'
+ bw9: '0'
+ clock_source0: ''
+ clock_source1: ''
+ clock_source2: ''
+ clock_source3: ''
+ clock_source4: ''
+ clock_source5: ''
+ clock_source6: ''
+ clock_source7: ''
+ comment: ''
+ corr0: '0'
+ corr1: '0'
+ corr10: '0'
+ corr11: '0'
+ corr12: '0'
+ corr13: '0'
+ corr14: '0'
+ corr15: '0'
+ corr16: '0'
+ corr17: '0'
+ corr18: '0'
+ corr19: '0'
+ corr2: '0'
+ corr20: '0'
+ corr21: '0'
+ corr22: '0'
+ corr23: '0'
+ corr24: '0'
+ corr25: '0'
+ corr26: '0'
+ corr27: '0'
+ corr28: '0'
+ corr29: '0'
+ corr3: '0'
+ corr30: '0'
+ corr31: '0'
+ corr4: '0'
+ corr5: '0'
+ corr6: '0'
+ corr7: '0'
+ corr8: '0'
+ corr9: '0'
+ dc_offset_mode0: '2'
+ dc_offset_mode1: '0'
+ dc_offset_mode10: '0'
+ dc_offset_mode11: '0'
+ dc_offset_mode12: '0'
+ dc_offset_mode13: '0'
+ dc_offset_mode14: '0'
+ dc_offset_mode15: '0'
+ dc_offset_mode16: '0'
+ dc_offset_mode17: '0'
+ dc_offset_mode18: '0'
+ dc_offset_mode19: '0'
+ dc_offset_mode2: '0'
+ dc_offset_mode20: '0'
+ dc_offset_mode21: '0'
+ dc_offset_mode22: '0'
+ dc_offset_mode23: '0'
+ dc_offset_mode24: '0'
+ dc_offset_mode25: '0'
+ dc_offset_mode26: '0'
+ dc_offset_mode27: '0'
+ dc_offset_mode28: '0'
+ dc_offset_mode29: '0'
+ dc_offset_mode3: '0'
+ dc_offset_mode30: '0'
+ dc_offset_mode31: '0'
+ dc_offset_mode4: '0'
+ dc_offset_mode5: '0'
+ dc_offset_mode6: '0'
+ dc_offset_mode7: '0'
+ dc_offset_mode8: '0'
+ dc_offset_mode9: '0'
+ freq0: center_freq
+ freq1: 100e6
+ freq10: 100e6
+ freq11: 100e6
+ freq12: 100e6
+ freq13: 100e6
+ freq14: 100e6
+ freq15: 100e6
+ freq16: 100e6
+ freq17: 100e6
+ freq18: 100e6
+ freq19: 100e6
+ freq2: 100e6
+ freq20: 100e6
+ freq21: 100e6
+ freq22: 100e6
+ freq23: 100e6
+ freq24: 100e6
+ freq25: 100e6
+ freq26: 100e6
+ freq27: 100e6
+ freq28: 100e6
+ freq29: 100e6
+ freq3: 100e6
+ freq30: 100e6
+ freq31: 100e6
+ freq4: 100e6
+ freq5: 100e6
+ freq6: 100e6
+ freq7: 100e6
+ freq8: 100e6
+ freq9: 100e6
+ gain0: rf_gain
+ gain1: '10'
+ gain10: '10'
+ gain11: '10'
+ gain12: '10'
+ gain13: '10'
+ gain14: '10'
+ gain15: '10'
+ gain16: '10'
+ gain17: '10'
+ gain18: '10'
+ gain19: '10'
+ gain2: '10'
+ gain20: '10'
+ gain21: '10'
+ gain22: '10'
+ gain23: '10'
+ gain24: '10'
+ gain25: '10'
+ gain26: '10'
+ gain27: '10'
+ gain28: '10'
+ gain29: '10'
+ gain3: '10'
+ gain30: '10'
+ gain31: '10'
+ gain4: '10'
+ gain5: '10'
+ gain6: '10'
+ gain7: '10'
+ gain8: '10'
+ gain9: '10'
+ gain_mode0: 'False'
+ gain_mode1: 'False'
+ gain_mode10: 'False'
+ gain_mode11: 'False'
+ gain_mode12: 'False'
+ gain_mode13: 'False'
+ gain_mode14: 'False'
+ gain_mode15: 'False'
+ gain_mode16: 'False'
+ gain_mode17: 'False'
+ gain_mode18: 'False'
+ gain_mode19: 'False'
+ gain_mode2: 'False'
+ gain_mode20: 'False'
+ gain_mode21: 'False'
+ gain_mode22: 'False'
+ gain_mode23: 'False'
+ gain_mode24: 'False'
+ gain_mode25: 'False'
+ gain_mode26: 'False'
+ gain_mode27: 'False'
+ gain_mode28: 'False'
+ gain_mode29: 'False'
+ gain_mode3: 'False'
+ gain_mode30: 'False'
+ gain_mode31: 'False'
+ gain_mode4: 'False'
+ gain_mode5: 'False'
+ gain_mode6: 'False'
+ gain_mode7: 'False'
+ gain_mode8: 'False'
+ gain_mode9: 'False'
+ if_gain0: '20'
+ if_gain1: '20'
+ if_gain10: '20'
+ if_gain11: '20'
+ if_gain12: '20'
+ if_gain13: '20'
+ if_gain14: '20'
+ if_gain15: '20'
+ if_gain16: '20'
+ if_gain17: '20'
+ if_gain18: '20'
+ if_gain19: '20'
+ if_gain2: '20'
+ if_gain20: '20'
+ if_gain21: '20'
+ if_gain22: '20'
+ if_gain23: '20'
+ if_gain24: '20'
+ if_gain25: '20'
+ if_gain26: '20'
+ if_gain27: '20'
+ if_gain28: '20'
+ if_gain29: '20'
+ if_gain3: '20'
+ if_gain30: '20'
+ if_gain31: '20'
+ if_gain4: '20'
+ if_gain5: '20'
+ if_gain6: '20'
+ if_gain7: '20'
+ if_gain8: '20'
+ if_gain9: '20'
+ iq_balance_mode0: '2'
+ iq_balance_mode1: '0'
+ iq_balance_mode10: '0'
+ iq_balance_mode11: '0'
+ iq_balance_mode12: '0'
+ iq_balance_mode13: '0'
+ iq_balance_mode14: '0'
+ iq_balance_mode15: '0'
+ iq_balance_mode16: '0'
+ iq_balance_mode17: '0'
+ iq_balance_mode18: '0'
+ iq_balance_mode19: '0'
+ iq_balance_mode2: '0'
+ iq_balance_mode20: '0'
+ iq_balance_mode21: '0'
+ iq_balance_mode22: '0'
+ iq_balance_mode23: '0'
+ iq_balance_mode24: '0'
+ iq_balance_mode25: '0'
+ iq_balance_mode26: '0'
+ iq_balance_mode27: '0'
+ iq_balance_mode28: '0'
+ iq_balance_mode29: '0'
+ iq_balance_mode3: '0'
+ iq_balance_mode30: '0'
+ iq_balance_mode31: '0'
+ iq_balance_mode4: '0'
+ iq_balance_mode5: '0'
+ iq_balance_mode6: '0'
+ iq_balance_mode7: '0'
+ iq_balance_mode8: '0'
+ iq_balance_mode9: '0'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ nchan: '1'
+ num_mboards: '1'
+ sample_rate: samp_rate
+ sync: sync
+ time_source0: ''
+ time_source1: ''
+ time_source2: ''
+ time_source3: ''
+ time_source4: ''
+ time_source5: ''
+ time_source6: ''
+ time_source7: ''
+ type: fc32
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: xmlrpc_server_0
+ id: xmlrpc_server
+ parameters:
+ addr: localhost
+ alias: ''
+ comment: ''
+ port: '8080'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+
+connections:
+- [blocks_complex_to_mag_squared_0, '0', blocks_moving_average_xx_0, '0']
+- [blocks_moving_average_xx_0, '0', blocks_nlog10_ff_0, '0']
+- [blocks_nlog10_ff_0, '0', blocks_probe_signal_x_0, '0']
+- [blocks_selector_0, '0', blocks_file_sink_0, '0']
+- [blocks_selector_0, '1', blocks_null_sink_0, '0']
+- [lora_rx_0, out, blocks_message_debug_0, print]
+- [low_pass_filter_0, '0', blocks_complex_to_mag_squared_0, '0']
+- [low_pass_filter_0, '0', blocks_selector_0, '0']
+- [low_pass_filter_0, '0', lora_rx_0, '0']
+- [osmosdr_source_0, '0', low_pass_filter_0, '0']
+
+metadata:
+ file_format: 1
+ grc_version: 3.10.12.0
diff --git a/examples/lora_quality_analyzer.py b/examples/lora_quality_analyzer.py
new file mode 100755
index 0000000..12c6c59
--- /dev/null
+++ b/examples/lora_quality_analyzer.py
@@ -0,0 +1,209 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+#
+# SPDX-License-Identifier: GPL-3.0
+#
+# GNU Radio Python Flow Graph
+# Title: LoRa Signal Quality Analyzer
+# Author: gr-mcp
+# Description: LoRa decoder with IQ recording and real-time signal power measurement.
+# GNU Radio version: 3.10.12.0
+
+from gnuradio import blocks
+from gnuradio import blocks, gr
+from gnuradio import filter
+from gnuradio.filter import firdes
+from gnuradio import gr
+from gnuradio.fft import window
+import sys
+import signal
+from argparse import ArgumentParser
+from gnuradio.eng_arg import eng_float, intx
+from gnuradio import eng_notation
+from xmlrpc.server import SimpleXMLRPCServer
+import threading
+import gnuradio.lora_sdr as lora_sdr
+import osmosdr
+import time
+
+
+
+
+class lora_quality_analyzer(gr.top_block):
+
+ def __init__(self):
+ gr.top_block.__init__(self, "LoRa Signal Quality Analyzer", catch_exceptions=True)
+ self.flowgraph_started = threading.Event()
+
+ ##################################################
+ # Variables
+ ##################################################
+ self.signal_power_db = signal_power_db = 0
+ self.samp_rate = samp_rate = 1000000
+ self.rf_gain = rf_gain = 40
+ self.recording_selector = recording_selector = 1
+ self.lora_sf = lora_sf = 7
+ self.lora_bw = lora_bw = 125000
+ self.iq_file = iq_file = "/tmp/iq_capture.cf32"
+ self.center_freq = center_freq = 915e6
+
+ ##################################################
+ # Blocks
+ ##################################################
+
+ self.blocks_probe_signal_x_0 = blocks.probe_signal_f()
+ self.xmlrpc_server_0 = SimpleXMLRPCServer(('localhost', 8080), allow_none=True)
+ self.xmlrpc_server_0.register_instance(self)
+ self.xmlrpc_server_0_thread = threading.Thread(target=self.xmlrpc_server_0.serve_forever)
+ self.xmlrpc_server_0_thread.daemon = True
+ self.xmlrpc_server_0_thread.start()
+ def _signal_power_db_probe():
+ self.flowgraph_started.wait()
+ while True:
+
+ val = self.blocks_probe_signal_x_0.level()
+ try:
+ try:
+ self.doc.add_next_tick_callback(functools.partial(self.set_signal_power_db,val))
+ except AttributeError:
+ self.set_signal_power_db(val)
+ except AttributeError:
+ pass
+ time.sleep(1.0 / (2))
+ _signal_power_db_thread = threading.Thread(target=_signal_power_db_probe)
+ _signal_power_db_thread.daemon = True
+ _signal_power_db_thread.start()
+ self.osmosdr_source_0 = osmosdr.source(
+ args="numchan=" + str(1) + " " + 'rtl=0'
+ )
+ self.osmosdr_source_0.set_time_unknown_pps(osmosdr.time_spec_t())
+ self.osmosdr_source_0.set_sample_rate(samp_rate)
+ self.osmosdr_source_0.set_center_freq(center_freq, 0)
+ self.osmosdr_source_0.set_freq_corr(0, 0)
+ self.osmosdr_source_0.set_dc_offset_mode(2, 0)
+ self.osmosdr_source_0.set_iq_balance_mode(2, 0)
+ self.osmosdr_source_0.set_gain_mode(False, 0)
+ self.osmosdr_source_0.set_gain(rf_gain, 0)
+ self.osmosdr_source_0.set_if_gain(20, 0)
+ self.osmosdr_source_0.set_bb_gain(20, 0)
+ self.osmosdr_source_0.set_antenna('', 0)
+ self.osmosdr_source_0.set_bandwidth(0, 0)
+ self.low_pass_filter_0 = filter.fir_filter_ccf(
+ 2,
+ firdes.low_pass(
+ 1,
+ samp_rate,
+ 200e3,
+ 50e3,
+ window.WIN_HAMMING,
+ 6.76))
+ self.lora_rx_0 = lora_sdr.lora_sdr_lora_rx( bw=lora_bw, cr=1, has_crc=True, impl_head=False, pay_len=255, samp_rate=(int(samp_rate/2)), sf=lora_sf, sync_word=[0x12], soft_decoding=True, ldro_mode=2, print_rx=[True,True])
+ self.blocks_selector_0 = blocks.selector(gr.sizeof_gr_complex*1,0,recording_selector)
+ self.blocks_selector_0.set_enabled(True)
+ self.blocks_null_sink_0 = blocks.null_sink(gr.sizeof_gr_complex*1)
+ self.blocks_nlog10_ff_0 = blocks.nlog10_ff(10, 1, 0)
+ self.blocks_moving_average_xx_0 = blocks.moving_average_ff(10000, (1.0/10000), 4000, 1)
+ self.blocks_message_debug_0 = blocks.message_debug(True, gr.log_levels.info)
+ self.blocks_file_sink_0 = blocks.file_sink(gr.sizeof_gr_complex*1, iq_file, False)
+ self.blocks_file_sink_0.set_unbuffered(False)
+ self.blocks_complex_to_mag_squared_0 = blocks.complex_to_mag_squared(1)
+
+
+ ##################################################
+ # Connections
+ ##################################################
+ self.msg_connect((self.lora_rx_0, 'out'), (self.blocks_message_debug_0, 'print'))
+ self.connect((self.blocks_complex_to_mag_squared_0, 0), (self.blocks_moving_average_xx_0, 0))
+ self.connect((self.blocks_moving_average_xx_0, 0), (self.blocks_nlog10_ff_0, 0))
+ self.connect((self.blocks_nlog10_ff_0, 0), (self.blocks_probe_signal_x_0, 0))
+ self.connect((self.blocks_selector_0, 0), (self.blocks_file_sink_0, 0))
+ self.connect((self.blocks_selector_0, 1), (self.blocks_null_sink_0, 0))
+ self.connect((self.low_pass_filter_0, 0), (self.blocks_complex_to_mag_squared_0, 0))
+ self.connect((self.low_pass_filter_0, 0), (self.blocks_selector_0, 0))
+ self.connect((self.low_pass_filter_0, 0), (self.lora_rx_0, 0))
+ self.connect((self.osmosdr_source_0, 0), (self.low_pass_filter_0, 0))
+
+
+ def get_signal_power_db(self):
+ return self.signal_power_db
+
+ def set_signal_power_db(self, signal_power_db):
+ self.signal_power_db = signal_power_db
+
+ def get_samp_rate(self):
+ return self.samp_rate
+
+ def set_samp_rate(self, samp_rate):
+ self.samp_rate = samp_rate
+ self.low_pass_filter_0.set_taps(firdes.low_pass(1, self.samp_rate, 200e3, 50e3, window.WIN_HAMMING, 6.76))
+ self.osmosdr_source_0.set_sample_rate(self.samp_rate)
+
+ def get_rf_gain(self):
+ return self.rf_gain
+
+ def set_rf_gain(self, rf_gain):
+ self.rf_gain = rf_gain
+ self.osmosdr_source_0.set_gain(self.rf_gain, 0)
+
+ def get_recording_selector(self):
+ return self.recording_selector
+
+ def set_recording_selector(self, recording_selector):
+ self.recording_selector = recording_selector
+ self.blocks_selector_0.set_output_index(self.recording_selector)
+
+ def get_lora_sf(self):
+ return self.lora_sf
+
+ def set_lora_sf(self, lora_sf):
+ self.lora_sf = lora_sf
+
+ def get_lora_bw(self):
+ return self.lora_bw
+
+ def set_lora_bw(self, lora_bw):
+ self.lora_bw = lora_bw
+
+ def get_iq_file(self):
+ return self.iq_file
+
+ def set_iq_file(self, iq_file):
+ self.iq_file = iq_file
+ self.blocks_file_sink_0.open(self.iq_file)
+
+ def get_center_freq(self):
+ return self.center_freq
+
+ def set_center_freq(self, center_freq):
+ self.center_freq = center_freq
+ self.osmosdr_source_0.set_center_freq(self.center_freq, 0)
+
+
+
+
+def main(top_block_cls=lora_quality_analyzer, options=None):
+ tb = top_block_cls()
+
+ def sig_handler(sig=None, frame=None):
+ tb.stop()
+ tb.wait()
+
+ sys.exit(0)
+
+ signal.signal(signal.SIGINT, sig_handler)
+ signal.signal(signal.SIGTERM, sig_handler)
+
+ tb.start()
+ tb.flowgraph_started.set()
+
+ try:
+ input('Press Enter to quit: ')
+ except EOFError:
+ pass
+ tb.stop()
+ tb.wait()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/multi_sf_lora_rx.grc b/examples/multi_sf_lora_rx.grc
new file mode 100644
index 0000000..6d736f8
--- /dev/null
+++ b/examples/multi_sf_lora_rx.grc
@@ -0,0 +1,588 @@
+options:
+ parameters:
+ author: gr-mcp
+ catch_exceptions: 'True'
+ category: '[GRC Hier Blocks]'
+ cmake_opt: ''
+ comment: ''
+ copyright: ''
+ description: Simultaneous LoRa decoder for SF7-SF12. 915MHz, BW125k.
+ gen_cmake: 'On'
+ gen_linking: dynamic
+ generate_options: no_gui
+ hier_block_src_path: '.:'
+ id: multi_sf_lora_rx
+ max_nouts: '0'
+ output_language: python
+ placement: (0,0)
+ qt_qss_theme: ''
+ realtime_scheduling: ''
+ run: 'True'
+ run_command: '{python} -u {filename}'
+ run_options: prompt
+ sizing_mode: fixed
+ thread_safe_setters: ''
+ title: Multi-SF LoRa Receiver
+ window_size: (1000,1000)
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [8, 8]
+ rotation: 0
+ state: enabled
+
+blocks:
+- name: center_freq
+ id: variable
+ parameters:
+ comment: ''
+ value: 915e6
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: lora_bw
+ id: variable
+ parameters:
+ comment: ''
+ value: '125000'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: rf_gain
+ id: variable
+ parameters:
+ comment: ''
+ value: '40'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: samp_rate
+ id: variable
+ parameters:
+ comment: ''
+ value: '1000000'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: blocks_message_debug_0
+ id: blocks_message_debug
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ en_uvec: 'True'
+ log_level: info
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: lora_rx_0
+ id: lora_rx
+ parameters:
+ affinity: ''
+ alias: ''
+ bw: lora_bw
+ comment: ''
+ cr: '1'
+ has_crc: 'True'
+ impl_head: 'False'
+ ldro: '2'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ pay_len: '255'
+ print_rx: '[True,True]'
+ samp_rate: int(samp_rate/2)
+ sf: '7'
+ soft_decoding: 'True'
+ sync_word: '[0x12]'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: lora_rx_1
+ id: lora_rx
+ parameters:
+ affinity: ''
+ alias: ''
+ bw: lora_bw
+ comment: ''
+ cr: '1'
+ has_crc: 'True'
+ impl_head: 'False'
+ ldro: '2'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ pay_len: '255'
+ print_rx: '[True,True]'
+ samp_rate: int(samp_rate/2)
+ sf: '8'
+ soft_decoding: 'True'
+ sync_word: '[0x12]'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: lora_rx_2
+ id: lora_rx
+ parameters:
+ affinity: ''
+ alias: ''
+ bw: lora_bw
+ comment: ''
+ cr: '1'
+ has_crc: 'True'
+ impl_head: 'False'
+ ldro: '2'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ pay_len: '255'
+ print_rx: '[True,True]'
+ samp_rate: int(samp_rate/2)
+ sf: '9'
+ soft_decoding: 'True'
+ sync_word: '[0x12]'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: lora_rx_3
+ id: lora_rx
+ parameters:
+ affinity: ''
+ alias: ''
+ bw: lora_bw
+ comment: ''
+ cr: '1'
+ has_crc: 'True'
+ impl_head: 'False'
+ ldro: '2'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ pay_len: '255'
+ print_rx: '[True,True]'
+ samp_rate: int(samp_rate/2)
+ sf: '10'
+ soft_decoding: 'True'
+ sync_word: '[0x12]'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: low_pass_filter_0
+ id: low_pass_filter
+ parameters:
+ affinity: ''
+ alias: ''
+ beta: '6.76'
+ comment: ''
+ cutoff_freq: 200e3
+ decim: '2'
+ gain: '1'
+ interp: '1'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ samp_rate: samp_rate
+ type: fir_filter_ccf
+ width: 50e3
+ win: window.WIN_HAMMING
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: osmosdr_source_0
+ id: osmosdr_source
+ parameters:
+ affinity: ''
+ alias: ''
+ ant0: ''
+ ant1: ''
+ ant10: ''
+ ant11: ''
+ ant12: ''
+ ant13: ''
+ ant14: ''
+ ant15: ''
+ ant16: ''
+ ant17: ''
+ ant18: ''
+ ant19: ''
+ ant2: ''
+ ant20: ''
+ ant21: ''
+ ant22: ''
+ ant23: ''
+ ant24: ''
+ ant25: ''
+ ant26: ''
+ ant27: ''
+ ant28: ''
+ ant29: ''
+ ant3: ''
+ ant30: ''
+ ant31: ''
+ ant4: ''
+ ant5: ''
+ ant6: ''
+ ant7: ''
+ ant8: ''
+ ant9: ''
+ args: rtl=0
+ bb_gain0: '20'
+ bb_gain1: '20'
+ bb_gain10: '20'
+ bb_gain11: '20'
+ bb_gain12: '20'
+ bb_gain13: '20'
+ bb_gain14: '20'
+ bb_gain15: '20'
+ bb_gain16: '20'
+ bb_gain17: '20'
+ bb_gain18: '20'
+ bb_gain19: '20'
+ bb_gain2: '20'
+ bb_gain20: '20'
+ bb_gain21: '20'
+ bb_gain22: '20'
+ bb_gain23: '20'
+ bb_gain24: '20'
+ bb_gain25: '20'
+ bb_gain26: '20'
+ bb_gain27: '20'
+ bb_gain28: '20'
+ bb_gain29: '20'
+ bb_gain3: '20'
+ bb_gain30: '20'
+ bb_gain31: '20'
+ bb_gain4: '20'
+ bb_gain5: '20'
+ bb_gain6: '20'
+ bb_gain7: '20'
+ bb_gain8: '20'
+ bb_gain9: '20'
+ bw0: '0'
+ bw1: '0'
+ bw10: '0'
+ bw11: '0'
+ bw12: '0'
+ bw13: '0'
+ bw14: '0'
+ bw15: '0'
+ bw16: '0'
+ bw17: '0'
+ bw18: '0'
+ bw19: '0'
+ bw2: '0'
+ bw20: '0'
+ bw21: '0'
+ bw22: '0'
+ bw23: '0'
+ bw24: '0'
+ bw25: '0'
+ bw26: '0'
+ bw27: '0'
+ bw28: '0'
+ bw29: '0'
+ bw3: '0'
+ bw30: '0'
+ bw31: '0'
+ bw4: '0'
+ bw5: '0'
+ bw6: '0'
+ bw7: '0'
+ bw8: '0'
+ bw9: '0'
+ clock_source0: ''
+ clock_source1: ''
+ clock_source2: ''
+ clock_source3: ''
+ clock_source4: ''
+ clock_source5: ''
+ clock_source6: ''
+ clock_source7: ''
+ comment: ''
+ corr0: '0'
+ corr1: '0'
+ corr10: '0'
+ corr11: '0'
+ corr12: '0'
+ corr13: '0'
+ corr14: '0'
+ corr15: '0'
+ corr16: '0'
+ corr17: '0'
+ corr18: '0'
+ corr19: '0'
+ corr2: '0'
+ corr20: '0'
+ corr21: '0'
+ corr22: '0'
+ corr23: '0'
+ corr24: '0'
+ corr25: '0'
+ corr26: '0'
+ corr27: '0'
+ corr28: '0'
+ corr29: '0'
+ corr3: '0'
+ corr30: '0'
+ corr31: '0'
+ corr4: '0'
+ corr5: '0'
+ corr6: '0'
+ corr7: '0'
+ corr8: '0'
+ corr9: '0'
+ dc_offset_mode0: '2'
+ dc_offset_mode1: '0'
+ dc_offset_mode10: '0'
+ dc_offset_mode11: '0'
+ dc_offset_mode12: '0'
+ dc_offset_mode13: '0'
+ dc_offset_mode14: '0'
+ dc_offset_mode15: '0'
+ dc_offset_mode16: '0'
+ dc_offset_mode17: '0'
+ dc_offset_mode18: '0'
+ dc_offset_mode19: '0'
+ dc_offset_mode2: '0'
+ dc_offset_mode20: '0'
+ dc_offset_mode21: '0'
+ dc_offset_mode22: '0'
+ dc_offset_mode23: '0'
+ dc_offset_mode24: '0'
+ dc_offset_mode25: '0'
+ dc_offset_mode26: '0'
+ dc_offset_mode27: '0'
+ dc_offset_mode28: '0'
+ dc_offset_mode29: '0'
+ dc_offset_mode3: '0'
+ dc_offset_mode30: '0'
+ dc_offset_mode31: '0'
+ dc_offset_mode4: '0'
+ dc_offset_mode5: '0'
+ dc_offset_mode6: '0'
+ dc_offset_mode7: '0'
+ dc_offset_mode8: '0'
+ dc_offset_mode9: '0'
+ freq0: center_freq
+ freq1: 100e6
+ freq10: 100e6
+ freq11: 100e6
+ freq12: 100e6
+ freq13: 100e6
+ freq14: 100e6
+ freq15: 100e6
+ freq16: 100e6
+ freq17: 100e6
+ freq18: 100e6
+ freq19: 100e6
+ freq2: 100e6
+ freq20: 100e6
+ freq21: 100e6
+ freq22: 100e6
+ freq23: 100e6
+ freq24: 100e6
+ freq25: 100e6
+ freq26: 100e6
+ freq27: 100e6
+ freq28: 100e6
+ freq29: 100e6
+ freq3: 100e6
+ freq30: 100e6
+ freq31: 100e6
+ freq4: 100e6
+ freq5: 100e6
+ freq6: 100e6
+ freq7: 100e6
+ freq8: 100e6
+ freq9: 100e6
+ gain0: rf_gain
+ gain1: '10'
+ gain10: '10'
+ gain11: '10'
+ gain12: '10'
+ gain13: '10'
+ gain14: '10'
+ gain15: '10'
+ gain16: '10'
+ gain17: '10'
+ gain18: '10'
+ gain19: '10'
+ gain2: '10'
+ gain20: '10'
+ gain21: '10'
+ gain22: '10'
+ gain23: '10'
+ gain24: '10'
+ gain25: '10'
+ gain26: '10'
+ gain27: '10'
+ gain28: '10'
+ gain29: '10'
+ gain3: '10'
+ gain30: '10'
+ gain31: '10'
+ gain4: '10'
+ gain5: '10'
+ gain6: '10'
+ gain7: '10'
+ gain8: '10'
+ gain9: '10'
+ gain_mode0: 'False'
+ gain_mode1: 'False'
+ gain_mode10: 'False'
+ gain_mode11: 'False'
+ gain_mode12: 'False'
+ gain_mode13: 'False'
+ gain_mode14: 'False'
+ gain_mode15: 'False'
+ gain_mode16: 'False'
+ gain_mode17: 'False'
+ gain_mode18: 'False'
+ gain_mode19: 'False'
+ gain_mode2: 'False'
+ gain_mode20: 'False'
+ gain_mode21: 'False'
+ gain_mode22: 'False'
+ gain_mode23: 'False'
+ gain_mode24: 'False'
+ gain_mode25: 'False'
+ gain_mode26: 'False'
+ gain_mode27: 'False'
+ gain_mode28: 'False'
+ gain_mode29: 'False'
+ gain_mode3: 'False'
+ gain_mode30: 'False'
+ gain_mode31: 'False'
+ gain_mode4: 'False'
+ gain_mode5: 'False'
+ gain_mode6: 'False'
+ gain_mode7: 'False'
+ gain_mode8: 'False'
+ gain_mode9: 'False'
+ if_gain0: '20'
+ if_gain1: '20'
+ if_gain10: '20'
+ if_gain11: '20'
+ if_gain12: '20'
+ if_gain13: '20'
+ if_gain14: '20'
+ if_gain15: '20'
+ if_gain16: '20'
+ if_gain17: '20'
+ if_gain18: '20'
+ if_gain19: '20'
+ if_gain2: '20'
+ if_gain20: '20'
+ if_gain21: '20'
+ if_gain22: '20'
+ if_gain23: '20'
+ if_gain24: '20'
+ if_gain25: '20'
+ if_gain26: '20'
+ if_gain27: '20'
+ if_gain28: '20'
+ if_gain29: '20'
+ if_gain3: '20'
+ if_gain30: '20'
+ if_gain31: '20'
+ if_gain4: '20'
+ if_gain5: '20'
+ if_gain6: '20'
+ if_gain7: '20'
+ if_gain8: '20'
+ if_gain9: '20'
+ iq_balance_mode0: '2'
+ iq_balance_mode1: '0'
+ iq_balance_mode10: '0'
+ iq_balance_mode11: '0'
+ iq_balance_mode12: '0'
+ iq_balance_mode13: '0'
+ iq_balance_mode14: '0'
+ iq_balance_mode15: '0'
+ iq_balance_mode16: '0'
+ iq_balance_mode17: '0'
+ iq_balance_mode18: '0'
+ iq_balance_mode19: '0'
+ iq_balance_mode2: '0'
+ iq_balance_mode20: '0'
+ iq_balance_mode21: '0'
+ iq_balance_mode22: '0'
+ iq_balance_mode23: '0'
+ iq_balance_mode24: '0'
+ iq_balance_mode25: '0'
+ iq_balance_mode26: '0'
+ iq_balance_mode27: '0'
+ iq_balance_mode28: '0'
+ iq_balance_mode29: '0'
+ iq_balance_mode3: '0'
+ iq_balance_mode30: '0'
+ iq_balance_mode31: '0'
+ iq_balance_mode4: '0'
+ iq_balance_mode5: '0'
+ iq_balance_mode6: '0'
+ iq_balance_mode7: '0'
+ iq_balance_mode8: '0'
+ iq_balance_mode9: '0'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ nchan: '1'
+ num_mboards: '1'
+ sample_rate: samp_rate
+ sync: sync
+ time_source0: ''
+ time_source1: ''
+ time_source2: ''
+ time_source3: ''
+ time_source4: ''
+ time_source5: ''
+ time_source6: ''
+ time_source7: ''
+ type: fc32
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+- name: xmlrpc_server_0
+ id: xmlrpc_server
+ parameters:
+ addr: localhost
+ alias: ''
+ comment: ''
+ port: '8080'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ state: enabled
+
+connections:
+- [lora_rx_0, out, blocks_message_debug_0, print]
+- [lora_rx_1, out, blocks_message_debug_0, print]
+- [lora_rx_2, out, blocks_message_debug_0, print]
+- [lora_rx_3, out, blocks_message_debug_0, print]
+- [low_pass_filter_0, '0', lora_rx_0, '0']
+- [low_pass_filter_0, '0', lora_rx_1, '0']
+- [low_pass_filter_0, '0', lora_rx_2, '0']
+- [low_pass_filter_0, '0', lora_rx_3, '0']
+- [osmosdr_source_0, '0', low_pass_filter_0, '0']
+
+metadata:
+ file_format: 1
+ grc_version: 3.10.12.0
diff --git a/examples/multi_sf_lora_rx.py b/examples/multi_sf_lora_rx.py
new file mode 100755
index 0000000..bf230a6
--- /dev/null
+++ b/examples/multi_sf_lora_rx.py
@@ -0,0 +1,155 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+#
+# SPDX-License-Identifier: GPL-3.0
+#
+# GNU Radio Python Flow Graph
+# Title: Multi-SF LoRa Receiver
+# Author: gr-mcp
+# Description: Simultaneous LoRa decoder for SF7-SF12. 915MHz, BW125k.
+# GNU Radio version: 3.10.12.0
+
+from gnuradio import blocks, gr
+from gnuradio import filter
+from gnuradio.filter import firdes
+from gnuradio import gr
+from gnuradio.fft import window
+import sys
+import signal
+from argparse import ArgumentParser
+from gnuradio.eng_arg import eng_float, intx
+from gnuradio import eng_notation
+from xmlrpc.server import SimpleXMLRPCServer
+import threading
+import gnuradio.lora_sdr as lora_sdr
+import osmosdr
+import time
+
+
+
+
+class multi_sf_lora_rx(gr.top_block):
+
+ def __init__(self):
+ gr.top_block.__init__(self, "Multi-SF LoRa Receiver", catch_exceptions=True)
+ self.flowgraph_started = threading.Event()
+
+ ##################################################
+ # Variables
+ ##################################################
+ self.samp_rate = samp_rate = 1000000
+ self.rf_gain = rf_gain = 40
+ self.lora_bw = lora_bw = 125000
+ self.center_freq = center_freq = 915e6
+
+ ##################################################
+ # Blocks
+ ##################################################
+
+ self.xmlrpc_server_0 = SimpleXMLRPCServer(('localhost', 8080), allow_none=True)
+ self.xmlrpc_server_0.register_instance(self)
+ self.xmlrpc_server_0_thread = threading.Thread(target=self.xmlrpc_server_0.serve_forever)
+ self.xmlrpc_server_0_thread.daemon = True
+ self.xmlrpc_server_0_thread.start()
+ self.osmosdr_source_0 = osmosdr.source(
+ args="numchan=" + str(1) + " " + 'rtl=0'
+ )
+ self.osmosdr_source_0.set_time_unknown_pps(osmosdr.time_spec_t())
+ self.osmosdr_source_0.set_sample_rate(samp_rate)
+ self.osmosdr_source_0.set_center_freq(center_freq, 0)
+ self.osmosdr_source_0.set_freq_corr(0, 0)
+ self.osmosdr_source_0.set_dc_offset_mode(2, 0)
+ self.osmosdr_source_0.set_iq_balance_mode(2, 0)
+ self.osmosdr_source_0.set_gain_mode(False, 0)
+ self.osmosdr_source_0.set_gain(rf_gain, 0)
+ self.osmosdr_source_0.set_if_gain(20, 0)
+ self.osmosdr_source_0.set_bb_gain(20, 0)
+ self.osmosdr_source_0.set_antenna('', 0)
+ self.osmosdr_source_0.set_bandwidth(0, 0)
+ self.low_pass_filter_0 = filter.fir_filter_ccf(
+ 2,
+ firdes.low_pass(
+ 1,
+ samp_rate,
+ 200e3,
+ 50e3,
+ window.WIN_HAMMING,
+ 6.76))
+ self.lora_rx_3 = lora_sdr.lora_sdr_lora_rx( bw=lora_bw, cr=1, has_crc=True, impl_head=False, pay_len=255, samp_rate=(int(samp_rate/2)), sf=10, sync_word=[0x12], soft_decoding=True, ldro_mode=2, print_rx=[True,True])
+ self.lora_rx_2 = lora_sdr.lora_sdr_lora_rx( bw=lora_bw, cr=1, has_crc=True, impl_head=False, pay_len=255, samp_rate=(int(samp_rate/2)), sf=9, sync_word=[0x12], soft_decoding=True, ldro_mode=2, print_rx=[True,True])
+ self.lora_rx_1 = lora_sdr.lora_sdr_lora_rx( bw=lora_bw, cr=1, has_crc=True, impl_head=False, pay_len=255, samp_rate=(int(samp_rate/2)), sf=8, sync_word=[0x12], soft_decoding=True, ldro_mode=2, print_rx=[True,True])
+ self.lora_rx_0 = lora_sdr.lora_sdr_lora_rx( bw=lora_bw, cr=1, has_crc=True, impl_head=False, pay_len=255, samp_rate=(int(samp_rate/2)), sf=7, sync_word=[0x12], soft_decoding=True, ldro_mode=2, print_rx=[True,True])
+ self.blocks_message_debug_0 = blocks.message_debug(True, gr.log_levels.info)
+
+
+ ##################################################
+ # Connections
+ ##################################################
+ self.msg_connect((self.lora_rx_0, 'out'), (self.blocks_message_debug_0, 'print'))
+ self.msg_connect((self.lora_rx_1, 'out'), (self.blocks_message_debug_0, 'print'))
+ self.msg_connect((self.lora_rx_2, 'out'), (self.blocks_message_debug_0, 'print'))
+ self.msg_connect((self.lora_rx_3, 'out'), (self.blocks_message_debug_0, 'print'))
+ self.connect((self.low_pass_filter_0, 0), (self.lora_rx_0, 0))
+ self.connect((self.low_pass_filter_0, 0), (self.lora_rx_1, 0))
+ self.connect((self.low_pass_filter_0, 0), (self.lora_rx_2, 0))
+ self.connect((self.low_pass_filter_0, 0), (self.lora_rx_3, 0))
+ self.connect((self.osmosdr_source_0, 0), (self.low_pass_filter_0, 0))
+
+
+ def get_samp_rate(self):
+ return self.samp_rate
+
+ def set_samp_rate(self, samp_rate):
+ self.samp_rate = samp_rate
+ self.low_pass_filter_0.set_taps(firdes.low_pass(1, self.samp_rate, 200e3, 50e3, window.WIN_HAMMING, 6.76))
+ self.osmosdr_source_0.set_sample_rate(self.samp_rate)
+
+ def get_rf_gain(self):
+ return self.rf_gain
+
+ def set_rf_gain(self, rf_gain):
+ self.rf_gain = rf_gain
+ self.osmosdr_source_0.set_gain(self.rf_gain, 0)
+
+ def get_lora_bw(self):
+ return self.lora_bw
+
+ def set_lora_bw(self, lora_bw):
+ self.lora_bw = lora_bw
+
+ def get_center_freq(self):
+ return self.center_freq
+
+ def set_center_freq(self, center_freq):
+ self.center_freq = center_freq
+ self.osmosdr_source_0.set_center_freq(self.center_freq, 0)
+
+
+
+
+def main(top_block_cls=multi_sf_lora_rx, options=None):
+ tb = top_block_cls()
+
+ def sig_handler(sig=None, frame=None):
+ tb.stop()
+ tb.wait()
+
+ sys.exit(0)
+
+ signal.signal(signal.SIGINT, sig_handler)
+ signal.signal(signal.SIGTERM, sig_handler)
+
+ tb.start()
+ tb.flowgraph_started.set()
+
+ try:
+ input('Press Enter to quit: ')
+ except EOFError:
+ pass
+ tb.stop()
+ tb.wait()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/uv.lock b/uv.lock
index 3c7c6bc..658d7cd 100644
--- a/uv.lock
+++ b/uv.lock
@@ -352,7 +352,7 @@ wheels = [
[[package]]
name = "gnuradio-mcp"
-version = "0.2.0"
+version = "2026.2.20"
source = { editable = "." }
dependencies = [
{ name = "fastmcp" },