diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..fa2396b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,24 @@ +# Changelog + +All notable changes to `doc-weaver` are documented here. + +## 0.1.0 + +### Added + +- TypeDoc-based loader (`runTypeDocJson`) producing serialized reflection JSON. +- `TypeDocJsonToMCP` renderer emitting a doc-forge-compatible MCP bundle: + `index.json`, `nav.json`, and per-module JSON under `modules/` with dotted + module paths and `docs://` resource URIs. +- `generateHumanDocs` renderer producing the TypeDoc HTML site (the MkDocs + parallel). +- `MCPServer` built on the official `@modelcontextprotocol/sdk`, exposing + `docs://index`, `docs://nav`, and `docs://modules/{module}` plus a `ping` + tool. +- `doc-weaver build` and `doc-weaver serve` CLI commands. +- React-aware kind classification (`component`, `hook`) and Google-style + docstring rendering. +- RDFC convention guide under `docs/wiki/02_rdfc.md`, plus a hand-written + wiki (`docs/wiki/`) built with MkDocs into `site/wiki/`. +- Self-documentation: `docs/mcp/` bundle and `site/lib/` TypeDoc site for + `doc-weaver` itself. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ebd3f82 --- /dev/null +++ b/README.md @@ -0,0 +1,186 @@ +# doc-weaver + +TypeScript documentation compiler that mirrors [`doc-forge`](../doc-forge). + +`doc-weaver` reads **TypeScript + JSDoc** and emits documentation for both +humans and machines: + +- **Humans** – a [TypeDoc](https://typedoc.org) site (the parallel to + doc-forge's MkDocs site). +- **Machines** – an MCP JSON bundle (`index.json`, `nav.json`, `modules/`) + whose shape is byte-compatible with the bundle `doc-forge` produces for + Python, so the same consumers (agents, `docs://` clients, the standards + registry) can query it unchanged. + +Where `doc-forge` uses `mkdocs` + Google-style docstrings (`GSDFC`), +`doc-weaver` uses `TypeDoc` + Google-style JSDoc (`RDFC`). + +--- + +## Parity map + +| Concern | Python (`doc-forge`) | TypeScript (`doc-weaver`) | +| --- | --- | --- | +| Source of truth | Python docstrings (GSDFC) | TypeScript types + JSDoc (RDFC) | +| Front-end (loader) | Griffe static analysis | TypeDoc (TypeScript compiler API) | +| Middle-end (model) | `Project -> Module -> DocObject` | `Project -> Module -> DocObject` | +| Human renderer | MkDocs (`site/lib`) | TypeDoc (`site/lib`) | +| Machine renderer | `MCPRenderer` JSON | `TypeDocJsonToMCP` JSON | +| Convention | GSDFC (Google-style docstrings) | RDFC (Google-style JSDoc) | +| CLI | `doc-forge build/serve` | `doc-weaver build/serve` | +| MCP server | `docforge.servers.MCPServer` | `docweaver.servers.MCPServer` | + +### MCP bundle (identical shape to doc-forge) + +``` +docs/mcp/ + index.json { project, type: "docforge-model", modules_count, source } + nav.json [ { module, resource: "docs://modules/" } ] + modules/.json { module, content: { path, docstring, objects } } +``` + +Each object is rendered recursively as: + +``` +{ name, kind, path, signature, docstring, members? } +``` + +Resource URIs use the same `docs://` scheme as doc-forge: + +- `docs://index` +- `docs://nav` +- `docs://modules/{module}` + +Module paths use **dotted** form (`components.ResourceList`) so a single URI +segment matches, exactly like Python's dotted module paths. + +--- + +## Installation + +```bash +npm install +npm run build +``` + +This produces the `doc-weaver` binary in `dist/` and `bin/`. + +--- + +## CLI usage + +```bash +# Machine-readable MCP JSON +doc-weaver build --mcp --module src/index.ts --project-name my-app + +# Human-facing TypeDoc site (MkDocs parallel) +doc-weaver build --docs --module src/index.ts --project-name my-app + +# Both in one pass +doc-weaver build --mcp --docs --module src/index.ts + +# Serve a generated MCP bundle +doc-weaver serve --mcp --module my-app --out-dir docs/mcp +``` + +Flags: + +| Flag | Meaning | Default | +| --- | --- | --- | +| `--mcp` | Emit MCP JSON bundle | – | +| `--docs` / `--mkdocs` / `--lib` | Emit the TypeDoc human site | – | +| `--module ` / `--entry ` | Entry point for TypeDoc | required | +| `--project-name ` / `--name ` | Project name | `package.json` name | +| `--tsconfig ` | tsconfig to use | auto-discovered | +| `--out-dir ` | MCP output directory | `docs/mcp` | +| `--out ` | Human docs output directory | `site/lib` | +| `--plugin ` | Extra TypeDoc plugins | – | +| `--strict` | Fail on TypeScript errors instead of generating anyway | off | + +By default doc-weaver **skips TypeScript error checking** (`skipErrorChecking`), +so documentation is produced even when a project has unrelated type errors. +Pass `--strict` to make errors fatal. + +`--api` and `--wiki` are reserved for parity with doc-forge and are not yet +implemented. + +--- + +## Adding it to a React package + +Add a script to the package's `package.json` (pointing at the package entry): + +```jsonc +{ + "scripts": { + "docs:mcp": "doc-weaver build --mcp --module index.ts --out-dir docs/mcp", + "docs:site": "doc-weaver build --docs --module index.ts --out site/lib", + "docs:serve": "doc-weaver serve --mcp --module my-app --out-dir docs/mcp" + } +} +``` + +Packages in this workspace (`react-openapi`, `react-auth`, `vet`) export their +public surface from `index.ts`, so a single `--module index.ts` documents the +package. To document **per-file modules**, pass a directory or several entry +points instead: + +```bash +doc-weaver build --mcp --module src +doc-weaver build --mcp --module src/a.ts --module src/b.ts +``` + +--- + +## Architecture + +``` +TypeScript + JSDoc + | + TypeDoc (loader) -> serialized reflection JSON + | + TypeDocJsonToMCP (renderer) -> MCP bundle + generateHumanDocs (renderer)-> TypeDoc HTML site +``` + +``` +src/ + cli/ commands, mcp-utils + loaders/ typedoc-loader (static analysis) + models/ semantic model + TypeDoc helpers + renderers/ mcp-renderer, human-docs, docstring, signature + servers/ mcp-server (official MCP SDK) +templates/ typedoc.json baseline +``` + +Like doc-forge, `doc-weaver` is renderer-agnostic: the MCP renderer and the +TypeDoc renderer never depend on each other, and no user code is executed to +produce documentation semantics. + +--- + +## Convention (RDFC) + +See [`docs/wiki/02_rdfc.md`](docs/wiki/02_rdfc.md) for the *React Doc-forge +Convention* (RDFC) — Google-style JSDoc that keeps docstrings the single source +of truth and makes them machine-parsable. + +The hand-written docs live under `docs/wiki/` and are built with MkDocs into +`site/wiki/` (mirroring doc-forge), while the API site is built by TypeDoc into +`site/lib/`: + +```bash +# Wiki (MkDocs) -> site/wiki/ +mkdocs build -f docs/mkdocs.wiki.yml + +# API site (TypeDoc) + MCP bundle for doc-weaver itself +node bin/doc-weaver.js build --mcp --docs \ + --module src --project-name doc-weaver \ + --out-dir docs/mcp --out site/lib +``` + +--- + +## License + +MIT diff --git a/bin/doc-weaver.js b/bin/doc-weaver.js new file mode 100644 index 0000000..c44b47a --- /dev/null +++ b/bin/doc-weaver.js @@ -0,0 +1,3 @@ +#!/usr/bin/env node +import { main } from '../dist/cli/index.js'; +main(); diff --git a/docs/mkdocs.wiki.yml b/docs/mkdocs.wiki.yml new file mode 100644 index 0000000..ce92179 --- /dev/null +++ b/docs/mkdocs.wiki.yml @@ -0,0 +1,63 @@ +theme: + name: material + palette: + scheme: slate + primary: blue grey + accent: teal + font: + text: Roboto + code: JetBrains Mono + features: + - navigation.sections + - navigation.expand + - navigation.top + - navigation.instant + - navigation.tracking + - navigation.indexes + - content.code.copy + - content.code.annotate + - content.tabs.link + - content.action.edit + - search.highlight + - search.share + - search.suggest + - navigation.tabs + - toc.integrate + - header.autohide + - content.code.select + - content.code.line_numbers + - content.tooltips + icon: + logo: material/file-document-multiple + repo: fontawesome/brands/github +markdown_extensions: +- pymdownx.superfences +- pymdownx.inlinehilite +- pymdownx.snippets +- admonition +- pymdownx.details +- pymdownx.highlight: + linenums: true + anchor_linenums: true + line_spans: __span + pygments_lang_class: true +- pymdownx.tabbed: + alternate_style: true +- pymdownx.tasklist: + custom_checkbox: true +- tables +- footnotes +- pymdownx.caret +- pymdownx.tilde +- pymdownx.mark +extra_css: +- https://unpkg.com/dracula-prism/dist/css/dracula-prism.css +plugins: +- search +site_name: doc-weaver +docs_dir: wiki +site_dir: ../site/wiki +nav: +- Home: index.md +- Overview: 01_overview.md +- RDFC Guide: 02_rdfc.md diff --git a/docs/wiki/01_overview.md b/docs/wiki/01_overview.md new file mode 100644 index 0000000..0e14c3c --- /dev/null +++ b/docs/wiki/01_overview.md @@ -0,0 +1,60 @@ +# Library Overview + +`doc-weaver` turns RDFC-compliant TypeScript JSDoc into maintainable reference +documentation. It never edits source comments; it reads them, renders them, and +assembles documentation from all available material — exactly as `doc-forge` +does for Python docstrings. + +Analysis is static: source is parsed by the TypeScript compiler API via TypeDoc +and is never executed. + +--- + +## 🧭 Which Build Should You Use? + +| Flag | Source | Output | +|-----------------------|-----------------------|---------------------------------------------------| +| `--docs` (`--mkdocs`, `--lib`) | JSDoc comments | `site/lib/**` TypeDoc HTML (the MkDocs parallel) | +| `--mcp` | JSDoc comments + model | `docs/mcp/**` doc-forge-shaped JSON | +| `--api` | OpenAPI spec | reserved | +| `--wiki` | Hand-written markdown | `docs/wiki/**` (unchanged) | + +--- + +## 🏗️ Architecture + +``` +TypeScript + JSDoc + | + TypeDoc (loader) -> serialized reflection JSON + | + TypeDocJsonToMCP (renderer) -> MCP bundle (docs/mcp/) + generateHumanDocs (renderer) -> TypeDoc HTML site (site/lib/) +``` + +The MCP renderer and the human renderer never depend on each other: both read +the same serialized reflections, so the machine bundle and the human site can +never disagree about a symbol. + +--- + +## 🔄 MCP bundle shape + +`docs/mcp/**` is a self-contained, machine-readable reference whose shape is +byte-compatible with `doc-forge`: + +``` +docs/mcp/ + index.json { project, type: "docforge-model", modules_count, source } + nav.json [ { module, resource: "docs://modules/" } ] + modules/.json { module, content: { path, docstring, objects } } +``` + +`doc-weaver serve --mcp --module doc-weaver` serves it over stdio. See the +[RDFC Guide](02_rdfc.md) for how to write the JSDoc that fills it. + +--- + +## ➡️ Read Next + +- [02 – RDFC Guide](02_rdfc.md) diff --git a/docs/wiki/02_rdfc.md b/docs/wiki/02_rdfc.md new file mode 100644 index 0000000..5edf111 --- /dev/null +++ b/docs/wiki/02_rdfc.md @@ -0,0 +1,173 @@ +# RDFC Guide + +RDFC — the **React Doc-forge Convention** — is the TypeScript counterpart to +doc-forge's **GSDFC**. This page is the practical companion to the convention: +how to write JSDoc so it renders correctly in the TypeDoc site **and** stays +machine-parsable in the MCP bundle and by AI tooling. + +--- + +## 🧭 Overview + +- Documentation is the single source of truth and lives in JSDoc comments. +- `doc-weaver` compiles comments; it never rewrites or invents them. +- Structure follows the TypeScript export hierarchy. +- Every public export should have a complete and accurate JSDoc comment. +- Types live in signatures; do not duplicate them in prose unless a tag + requires it. + +--- + +## 📦 Module (file) level + +Document the file with a leading block comment. It becomes the module +`docstring` in the MCP bundle. + +```ts +/** + * # Resource list + * + * Renders, filters, and paginates a resource collection. + * + * --- + * + * Notes: + * - Reads configuration from the OpenAPI spec. + * - Pagination is server-driven. + */ +``` + +--- + +## 📦 Components + +Document the component function and its props. Components are classified +automatically as `kind: "component"` (PascalCase name). + +```ts +/** + * Render a single resource row with actions. + * + * @param props - Component props. + * @param props.item - The resource record to display. + * @param props.onSelect - Invoked when the row is selected. + * @returns The rendered row element. + * + * @example + * ```tsx + * + * ``` + */ +export function ResourceList(props: ListCellProps) { + // ... +} +``` + +--- + +## 📦 Hooks + +Hooks are classified automatically as `kind: "hook"` (name starts with `use`). + +```ts +/** + * Subscribe to server-sent events for an item. + * + * @param url - SSE endpoint, or `null` to disable. + * @param handlers - Event handlers keyed by event name. + * @returns Connection state and the latest payload. + * + * @remarks + * The subscription is torn down on unmount. + */ +export function useItemSse(url: string | null, handlers: ItemSseHandlers) { + // ... +} +``` + +--- + +## 📦 Types, interfaces, and members + +Document the type and each member. Members become nested `members` entries in +the MCP bundle. + +```ts +/** + * A single display field extracted from an OpenAPI schema. + */ +export interface FieldConfig { + /** Property name in the payload. */ + name: string; + /** `x-edit-as: date` — edit a `date-time` field with a date input. */ + editAs?: string; + /** Schema-level `default` used to seed create-form state. */ + defaultValue?: unknown; +} +``` + +--- + +## 📦 Notes subsection grouping + +Group related information using labeled subsections (Markdown bold): + +``` +Notes: + **Guarantees:** + + - deterministic output + + **Lifecycle:** + + - created during initialization + - reused across renders +``` + +--- + +## 📦 Heading map + +`doc-weaver` renders JSDoc block tags into stable markdown headings so both +TypeDoc and the MCP bundle agree: + +| JSDoc tag | Rendered heading | +|-----------|------------------| +| `@param` | `**Parameters:**` | +| `@returns` / `@return` | `**Returns:**` | +| `@throws` | `**Throws:**` | +| `@example` | `**Example:**` | +| `@deprecated` | `**Deprecated:**` | +| `@see` | `**See also:**` | +| `@remarks` | `**Remarks:**` | +| *other* | `**@tag:**` | + +--- + +## 📦 Mapping to output kinds + +| TS construct | `kind` | +|--------------|--------| +| interface / type alias | `interface` / `type-alias` | +| function component (PascalCase) | `component` | +| custom hook (`use*`) | `hook` | +| plain function | `function` | +| object / class member | `property` / `method` | +| const / let / var | `variable` | + +An explicit `@kind` tag overrides the inferred kind. + +--- + +## 🛡️ Enforcement + +Unlike doc-forge (which runs `pydoclint` in CI), `doc-weaver` performs no +signature linting: TypeDoc extracts whatever JSDoc is present. Keep comments +complete and accurate — they are the single source of truth for both the human +site and the machine bundle. + +--- + +## Related + +- [01 – Overview](01_overview.md) diff --git a/docs/wiki/index.md b/docs/wiki/index.md new file mode 100644 index 0000000..283e72e --- /dev/null +++ b/docs/wiki/index.md @@ -0,0 +1,55 @@ +# 🪡 doc-weaver — TypeScript Documentation Weaver + +`doc-weaver` is the TypeScript counterpart to `doc-forge`. It compiles +JSDoc-annotated TypeScript into a **TypeDoc** site (the MkDocs parallel) and a +**doc-forge-shaped MCP JSON bundle**, so React packages document themselves for +humans and agents alike. + +> **Doc model:** this wiki is written for humans — guides and conventions. The +> authoritative API contracts live in the code (JSDoc) and the machine-readable +> bundle under `docs/mcp/`. + +--- + +## 🚀 Key Features + +- 🧵 JSDoc extraction via TypeDoc (the TypeScript compiler API) +- 🧬 doc-forge-compatible MCP bundle (`index.json`, `nav.json`, `modules/*.json`) +- ⚛️ React-aware kinds — `component` (PascalCase) and `hook` (`use*`) +- 📚 RDFC (React Doc-forge Convention) — the JSDoc parallel to GSDFC +- 🔌 MCP server over stdio exposing `docs://index`, `docs://nav`, and + `docs://modules/{module}` + +--- + +## 📦 Installation + +```bash +npm install +npm run build +``` + +This produces the `doc-weaver` binary under `bin/` and `dist/`. + +--- + +## 📁 Documentation Structure + +| Section | Description | +|-----------------------------------------|------------------------------------------------| +| [Overview](01_overview.md) | What doc-weaver is and how it fits | +| [RDFC Guide](02_rdfc.md) | How to write RDFC-conformant JSDoc | + +--- + +## 🔗 Related Resources + +- **Source Code:** the `doc-weaver` repository +- **MCP Bundle:** generated by `doc-weaver build --mcp --module src/index.ts` + into `docs/mcp/` +- **Library Registry:** registered in the standards hub as `doc-weaver` + (MCP `:8010`) + +--- + +© Aetoskia Internal — `doc-weaver` 0.1.0 diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..2e27e6e --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1903 @@ +{ + "name": "doc-weaver", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "doc-weaver", + "version": "0.1.0", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.17.4", + "commander": "^12.1.0", + "fs-extra": "^11.3.1", + "typedoc": "^0.27.9" + }, + "bin": { + "doc-weaver": "bin/doc-weaver.js" + }, + "devDependencies": { + "@types/fs-extra": "^11.0.4", + "@types/node": "^20.19.11", + "tsx": "^4.20.5", + "typescript": "~5.6.3" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@gerrit0/mini-shiki": { + "version": "1.27.2", + "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-1.27.2.tgz", + "integrity": "sha512-GeWyHz8ao2gBiUW4OJnQDxXQnFgZQwwQk05t/CVVgNBN7/rK8XZ7xY6YhLVv9tH3VppWWmr9DCl3MwemB/i+Og==", + "dependencies": { + "@shikijs/engine-oniguruma": "^1.27.2", + "@shikijs/types": "^1.27.2", + "@shikijs/vscode-textmate": "^10.0.1" + } + }, + "node_modules/@hono/node-server": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.5.tgz", + "integrity": "sha512-MgP/I06GdvsilDspMk/qmRzpk8LOUStP6jFAbSRi40W09DhoyUn+Mj7eyKICPqeYKybOjVFLVIAQzvu1bfIa7Q==", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "hono": "^4 || ^5.0.0-0" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.32.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.32.1.tgz", + "integrity": "sha512-2DdE+SJDtzLEEWzY1ZjY7Q+VcPhcV1KisD3zI4u0XZyktsjHum1mwbMI+JaulUBi2OZk+KJAi2uPXzxichPkdw==", + "dependencies": { + "@hono/node-server": "^1.19.9 || ^2.0.5", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "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==", + "dependencies": { + "@shikijs/types": "1.29.2", + "@shikijs/vscode-textmate": "^10.0.1" + } + }, + "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==", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.1", + "@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==" + }, + "node_modules/@types/fs-extra": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", + "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", + "dev": true, + "dependencies": { + "@types/jsonfile": "*", + "@types/node": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/jsonfile": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", + "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", + "dev": true, + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "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==" + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "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==" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/body-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/brace-expansion": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "engines": { + "node": ">=18" + } + }, + "node_modules/content-disposition": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-2.0.1.tgz", + "integrity": "sha512-e+H0ZXHSWYrENhQzw1LPuP4oF5MzVKmDU6d3hxlvaPEYLLg62MxtQNPRx4SYSuYJSBUgnQIG4HIN2tEtNv7Dog==", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz", + "integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/express": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.3.0.tgz", + "integrity": "sha512-qCqy1xSoaugxOD4IVHb1ZWdkvCF2W3aE2okKouWj8VwJ+ztskSiXoBnCSJqfKT+cVPJhuJqe9YLbY425+Zh00g==", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.3.0", + "content-disposition": "^2.0.1", + "content-type": "^2.0.0", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.8", + "qs": "^6.16.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.1.0", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.1.tgz", + "integrity": "sha512-2FXbqkygil3mZiXzEtM0IjE/UBmBNot0J1GEVrJT4JbrvwOSoEtqtO+wHbiMQELWe1eNvHoYRg2dkjOid+6JkA==", + "dependencies": { + "debug": "^4.4.3", + "ip-address": "^10.2.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/express/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-uri": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-extra": { + "version": "11.4.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.1.tgz", + "integrity": "sha512-KYAb4c9BJQI6QqGKthV68OHe0badztdXJWKo0WtBA9IuCFPTKvE5ZdUBglP833aMjhaSPNO4A5j/EkzZtGlKjA==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hono": { + "version": "4.13.13", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.13.tgz", + "integrity": "sha512-CQ46U0ZkAGmbT/4UxdzzGJpacP2IeKgY4a5/tOI9AABbpOMfK739wfDXmv1usCk+3RkKj1hQy4/fjhiwa2xlrA==", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ip-address": { + "version": "10.7.4", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.4.tgz", + "integrity": "sha512-DWBMKyeM25D70nrIiTkgQth59/G+naKOTep6huF9j+GmeK3DMD60Zvxg5d5qQnz+38AHgou6ean2LECX+lrKDg==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/jose": { + "version": "6.2.12", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.12.tgz", + "integrity": "sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==" + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/linkify-it": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz", + "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==" + }, + "node_modules/markdown-it": { + "version": "14.3.3", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.3.tgz", + "integrity": "sha512-L//tDAkjTjJCU6pBUSBTgs3Kg4InVaZuUZog/b6V12UKhcDQ7S78877ibbCanRExwPuYP1CZD7Gd2dxSy1GXRg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.5.0", + "linkify-it": "^5.0.2", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdurl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.1.0.tgz", + "integrity": "sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg==" + }, + "node_modules/media-typer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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==" + }, + "node_modules/negotiator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", + "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", + "dependencies": { + "content-type": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/negotiator/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.1.tgz", + "integrity": "sha512-5Px6J7rfHBAGUhNQ3es4YzcOdBBw0SDcVtThJml4RdnKw4tKs2epA0kPZ+K8X+WiHktT93RHVcQ/3ym5aTCgtg==", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", + "engines": { + "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tsx": { + "version": "4.23.15", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz", + "integrity": "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==", + "dev": true, + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/typedoc": { + "version": "0.27.9", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.27.9.tgz", + "integrity": "sha512-/z585740YHURLl9DN2jCWe6OW7zKYm6VoQ93H0sxZ1cwHQEQrUn5BJrEnkWhfzUdyO+BLGjnKUZ9iz9hKloFDw==", + "dependencies": { + "@gerrit0/mini-shiki": "^1.24.0", + "lunr": "^2.3.9", + "markdown-it": "^14.1.0", + "minimatch": "^9.0.5", + "yaml": "^2.6.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x" + } + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==" + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/yaml": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/zod": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.7.tgz", + "integrity": "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..d873082 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "doc-weaver", + "version": "0.1.0", + "type": "module", + "description": "TypeScript documentation compiler using TypeDoc + JSDoc (Google-style), generating MkDocs and MCP JSON in doc-forge shape.", + "bin": { + "doc-weaver": "./bin/doc-weaver.js" + }, + "scripts": { + "build": "tsc -p tsconfig.json", + "typecheck": "tsc -p tsconfig.json --noEmit", + "prepare": "tsc -p tsconfig.json", + "dev": "tsx src/cli/index.ts", + "start": "node bin/doc-weaver.js" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.17.4", + "commander": "^12.1.0", + "fs-extra": "^11.3.1", + "typedoc": "^0.27.9" + }, + "devDependencies": { + "@types/fs-extra": "^11.0.4", + "@types/node": "^20.19.11", + "tsx": "^4.20.5", + "typescript": "~5.6.3" + } +} diff --git a/src/cli/commands.ts b/src/cli/commands.ts new file mode 100644 index 0000000..e506c5f --- /dev/null +++ b/src/cli/commands.ts @@ -0,0 +1,138 @@ +import { Command } from 'commander'; +import * as path from 'path'; +import fs from 'fs-extra'; +import { runTypeDocJson } from '../loaders/typedoc-loader.js'; +import { generateHumanDocs } from '../renderers/human-docs.js'; +import { generateMcpResources, serveMcp } from './mcp-utils.js'; + +/** + * Build documentation. + * + * Mirrors doc-forge's `build` command: + * doc-weaver build --mcp --docs --module + */ +export function buildCommand(): Command { + return new Command('build') + .description('Build documentation (MCP JSON and/or a TypeDoc human site)') + .option('--mcp', 'Generate MCP JSON resources (index/nav/modules)') + .option('--docs', 'Generate the human-facing TypeDoc site') + .option('--mkdocs', 'Alias for --docs (MkDocs parallel)') + .option('--lib', 'Alias for --docs (library site)') + .option('--api', 'API docs from an OpenAPI spec (reserved)') + .option('--wiki', 'Hand-written wiki build (reserved)') + .option('--module ', 'Entry point (file or directory) for TypeDoc') + .option('--entry ', 'Entry file (alternative to --module)') + .option('--project-name ', 'Project name override') + .option('--name ', 'Site name (alias of --project-name)') + .option('--tsconfig ', 'tsconfig.json path') + .option('--out-dir ', 'MCP output directory', 'docs/mcp') + .option('--out ', 'Human docs output directory', 'site/lib') + .option('--plugin ', 'TypeDoc plugins to enable') + .option('--strict', 'Fail on TypeScript errors instead of generating anyway') + .action(async (opts) => { + const entry = opts.module || opts.entry; + if (!entry) { + throw new Error('Must specify --module or --entry'); + } + + const entryAbs = path.resolve(entry); + const projectName = + opts.projectName || + opts.name || + (await inferProjectName(entryAbs)); + + const wantMcp = opts.mcp; + const wantDocs = opts.docs || opts.mkdocs || opts.lib; + const wantApi = opts.api; + const wantWiki = opts.wiki; + + if (!wantMcp && !wantDocs && !wantApi && !wantWiki) { + throw new Error('Must specify at least one of --mcp, --docs, --api, --wiki'); + } + if (wantApi || wantWiki) { + throw new Error('--api and --wiki are reserved and not yet implemented'); + } + + const tsconfig = resolveTsconfig(entryAbs, opts.tsconfig); + // Directory entries expand to every file; a single file uses resolve. + const entryPointStrategy = fs.statSync(entryAbs).isDirectory() ? 'expand' : 'resolve'; + + if (wantMcp) { + const json = await runTypeDocJson({ + entryPoints: [entryAbs], + tsconfig, + name: projectName, + skipErrorChecking: !opts.strict, + entryPointStrategy + }); + const outDir = path.resolve(opts.outDir); + await generateMcpResources(json, projectName, outDir); + console.log(`MCP resources written to ${outDir}`); + } + + if (wantDocs) { + const out = await generateHumanDocs({ + entryPoints: [entryAbs], + tsconfig, + name: projectName, + out: path.resolve(opts.out), + plugin: opts.plugin, + skipErrorChecking: !opts.strict, + entryPointStrategy + }); + console.log(`Human docs written to ${out}`); + } + }); +} + +/** Serve documentation locally. */ +export function serveCommand(): Command { + return new Command('serve') + .description('Serve documentation locally') + .option('--mcp', 'Serve the generated MCP bundle') + .option('--module ', 'Module name used to label the MCP server') + .option('--out-dir ', 'MCP root directory', 'docs/mcp') + .action(async (opts) => { + if (!opts.mcp) { + throw new Error('Must specify --mcp'); + } + if (!opts.module) { + throw new Error('--module is required for MCP serve'); + } + await serveMcp(opts.module, path.resolve(opts.outDir)); + }); +} + +function resolveTsconfig(entryAbs: string, explicit?: string): string | undefined { + if (explicit) { + return fs.existsSync(explicit) ? path.resolve(explicit) : undefined; + } + let dir = path.dirname(entryAbs); + for (let i = 0; i < 6; i++) { + const candidate = path.join(dir, 'tsconfig.json'); + if (fs.existsSync(candidate)) return candidate; + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + return undefined; +} + +async function inferProjectName(entryAbs: string): Promise { + let dir = path.dirname(entryAbs); + for (let i = 0; i < 6; i++) { + const pkg = path.join(dir, 'package.json'); + if (await fs.pathExists(pkg)) { + try { + const data = await fs.readJSON(pkg); + if (data?.name) return data.name; + } catch { + // ignore malformed package.json + } + } + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + return path.basename(path.dirname(entryAbs)); +} diff --git a/src/cli/index.ts b/src/cli/index.ts new file mode 100644 index 0000000..80c3813 --- /dev/null +++ b/src/cli/index.ts @@ -0,0 +1,24 @@ +import { Command } from 'commander'; +import { pathToFileURL } from 'url'; +import { buildCommand, serveCommand } from './commands.js'; + +export function main(): void { + const program = new Command(); + program + .name('doc-weaver') + .description('TypeScript documentation compiler: MkDocs + MCP (doc-forge compatible)') + .version('0.1.0'); + + program.addCommand(buildCommand()); + program.addCommand(serveCommand()); + + program.parseAsync(process.argv).catch((err) => { + console.error(err); + process.exit(1); + }); +} + +// Allow `tsx src/cli/index.ts` and `node dist/cli/index.js` to run directly. +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + main(); +} diff --git a/src/cli/mcp-utils.ts b/src/cli/mcp-utils.ts new file mode 100644 index 0000000..bcbc1fd --- /dev/null +++ b/src/cli/mcp-utils.ts @@ -0,0 +1,41 @@ +import { TypeDocJsonToMCP } from '../renderers/mcp-renderer.js'; +import { MCPServer } from '../servers/mcp-server.js'; +import * as path from 'path'; +import fs from 'fs-extra'; + +/** Generate the doc-forge-compatible MCP bundle from TypeDoc reflection JSON. */ +export async function generateMcpResources( + typedocJson: any, + projectName: string | undefined, + outDir: string +): Promise { + const transformer = new TypeDocJsonToMCP(typedocJson, projectName); + const { index, nav, modules } = transformer.transform(); + + await fs.ensureDir(path.join(outDir, 'modules')); + await fs.writeJSON(path.join(outDir, 'index.json'), index, { spaces: 2 }); + await fs.writeJSON(path.join(outDir, 'nav.json'), nav, { spaces: 2 }); + + for (const [modPath, modData] of Object.entries(modules)) { + const outPath = path.join(outDir, 'modules', modPath + '.json'); + await fs.ensureDir(path.dirname(outPath)); + await fs.writeJSON(outPath, modData, { spaces: 2 }); + } +} + +/** Serve a pre-generated MCP bundle over stdio/HTTP. */ +export async function serveMcp(module: string, mcpRoot: string): Promise { + const root = path.resolve(mcpRoot); + const required = [ + path.join(root, 'index.json'), + path.join(root, 'nav.json'), + path.join(root, 'modules') + ]; + for (const r of required) { + if (!(await fs.pathExists(r))) { + throw new Error('Invalid MCP bundle, missing: ' + path.basename(r)); + } + } + const server = new MCPServer(root, module + '-mcp'); + await server.run(); +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..ebd6a91 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,29 @@ +/** + * # doc-weaver + * + * Public library surface for doc-weaver, the TypeScript counterpart to + * `doc-forge`. It compiles TypeDoc reflection (built from TypeScript + JSDoc) + * into two renderers: + * + * - a human-facing TypeDoc site (the MkDocs parallel), and + * - a machine-facing MCP JSON bundle (`index.json`, `nav.json`, `modules/`) + * whose shape is byte-compatible with `doc-forge`. + * + * Programmatic usage: + * + * import { runTypeDocJson, TypeDocJsonToMCP } from 'doc-weaver'; + * const json = await runTypeDocJson({ entryPoints: ['src/index.ts'] }); + * const bundle = new TypeDocJsonToMCP(json, 'my-app').transform(); + */ + +export { main } from './cli/index.js'; +export { runTypeDocJson, defaultTsconfigFor } from './loaders/typedoc-loader.js'; +export type { TypeDocOptions } from './loaders/typedoc-loader.js'; +export { generateHumanDocs } from './renderers/human-docs.js'; +export type { HumanDocsOptions } from './renderers/human-docs.js'; +export { TypeDocJsonToMCP } from './renderers/mcp-renderer.js'; +export type { McpBundle } from './renderers/mcp-renderer.js'; +export { generateMcpResources, serveMcp } from './cli/mcp-utils.js'; +export { MCPServer } from './servers/mcp-server.js'; +export { collectModules, resolveModulePath, RefKind } from './models/typedoc.js'; +export type { DocObject, DocModule, DocProject, DocKind } from './models/index.js'; diff --git a/src/loaders/typedoc-loader.ts b/src/loaders/typedoc-loader.ts new file mode 100644 index 0000000..5fbfd23 --- /dev/null +++ b/src/loaders/typedoc-loader.ts @@ -0,0 +1,61 @@ +import { Application, TSConfigReader } from 'typedoc'; +import * as path from 'path'; + +export interface TypeDocOptions { + entryPoints: string[]; + tsconfig?: string; + name?: string; + /** Optional TypeDoc theme/plugin passthroughs */ + plugin?: string[]; + /** + * Skip TypeScript error checking before conversion. Defaults to true so + * documentation still generates when a project has unrelated type errors. + */ + skipErrorChecking?: boolean; + /** TypeDoc entry-point strategy. Defaults to 'resolve'. */ + entryPointStrategy?: 'resolve' | 'expand' | 'merge' | 'packages'; +} + +/** + * Run TypeDoc programmatically and return the serialized reflection JSON. + * + * Mirrors doc-forge's static loader: no user code is executed for + * documentation semantics; we rely on TypeScript's compiler API. + */ +export async function runTypeDocJson(opts: TypeDocOptions): Promise { + const basePath = opts.tsconfig ? path.dirname(opts.tsconfig) : opts.entryPoints[0]; + const app = await Application.bootstrapWithPlugins( + { + entryPoints: opts.entryPoints, + tsconfig: opts.tsconfig, + name: opts.name, + plugin: opts.plugin, + basePath, + // Keep things deterministic and quiet + readme: 'none', + entryPointStrategy: opts.entryPointStrategy ?? 'resolve', + excludePrivate: false, + excludeInternal: false, + skipErrorChecking: opts.skipErrorChecking ?? true + }, + [new TSConfigReader()] + ); + + const project = await app.convert(); + if (!project) { + throw new Error('TypeDoc failed to produce a project reflection'); + } + + // TypeDoc >= 0.26 exposes serializer.projectToObject(root, projectRoot). + const serializer: any = (app as any).serializer; + if (serializer && typeof serializer.projectToObject === 'function') { + return serializer.projectToObject(project, basePath); + } + + throw new Error('Unsupported TypeDoc version: serializer.projectToObject is unavailable'); +} + +/** Convenience: resolve tsconfig relative to a given project root. */ +export function defaultTsconfigFor(root: string): string { + return path.join(root, 'tsconfig.json'); +} diff --git a/src/models/index.ts b/src/models/index.ts new file mode 100644 index 0000000..0d75674 --- /dev/null +++ b/src/models/index.ts @@ -0,0 +1,52 @@ +/** + * Semantic documentation model for doc-weaver. + * + * This mirrors doc-forge's renderer-agnostic model: a project is a + * collection of modules, each module contains named documentation objects, + * and objects may contain nested members. Renderers (MkDocs, MCP) consume + * this model so that output formats never depend on each other. + */ + +/** The set of documentable symbol kinds recognized by doc-weaver. */ +export type DocKind = + | 'module' + | 'namespace' + | 'class' + | 'interface' + | 'enum' + | 'enum-member' + | 'type-alias' + | 'variable' + | 'function' + | 'method' + | 'accessor' + | 'property' + | 'constructor' + | 'signature' + | 'parameter' + | 'component' + | 'hook' + | 'unknown'; + +/** A documentation object with an optional tree of members. */ +export interface DocObject { + name: string; + kind: DocKind; + path: string; + signature: string; + docstring: string; + members?: Record; +} + +/** A single module (typically one source file or package). */ +export interface DocModule { + path: string; + docstring: string; + objects: Record; +} + +/** The root semantic model produced by the loader. */ +export interface DocProject { + name: string; + modules: DocModule[]; +} diff --git a/src/models/typedoc.ts b/src/models/typedoc.ts new file mode 100644 index 0000000..5150384 --- /dev/null +++ b/src/models/typedoc.ts @@ -0,0 +1,66 @@ +import { ReflectionKind } from 'typedoc'; + +/** Re-export TypeDoc's numeric reflection kinds for internal use. */ +export const RefKind = ReflectionKind; + +/** + * Recursively collect every Module-kind reflection in a TypeDoc project. + * + * TypeDoc emits Module reflections for each resolved entry point (one per + * source file/package). When a project has no module children (a single flat + * entry point), the project itself is treated as a single module so that the + * output always has at least one module - matching doc-forge's model. + */ +export function collectModules(project: any): any[] { + const modules: any[] = []; + const walk = (node: any) => { + if (!node) return; + if (node.kind === RefKind.Module) { + modules.push(node); + return; + } + for (const child of node.children || []) walk(child); + }; + walk(project); + + if (modules.length > 0) return modules; + return [project]; +} + +/** Derive a stable, filesystem-safe module path from a TypeDoc reflection. */ +export function resolveModulePath(mod: any, project: any): string { + const source = mod.sources?.[0]?.fileName; + if (typeof source === 'string' && source.length) { + return normalizeSourcePath(source); + } + if (mod.kind === RefKind.Project) { + return sanitize(mod.name || project?.name || 'project'); + } + return sanitize(mod.name || 'module'); +} + +function normalizeSourcePath(fileName: string): string { + let p = fileName.replace(/\\/g, '/'); + p = p.replace(/^\.\//, ''); + p = p.replace(/^src\//, ''); + p = p.replace(/\.(tsx?|jsx?|mts|cts|mjs|cjs)$/, ''); + p = p.replace(/\/index$/, ''); + // Use dotted paths so MCP resource templates match a single URI segment, + // mirroring doc-forge's dotted Python module paths. + p = p.replace(/\//g, '.'); + return sanitize(p); +} + +/** Replace characters that are unsafe in a resource path / filename. */ +export function sanitize(value: string): string { + return value + .replace(/\\/g, '.') + .replace(/[^A-Za-z0-9_.-]/g, '_') + .replace(/^\.+/, '') + .replace(/\.+$/, ''); +} + +/** Human-readable name for a reflective kind (used in diagnostics). */ +export function reflectionKindName(kind: number): string { + return ReflectionKind[kind] ?? String(kind); +} diff --git a/src/renderers/docstring.ts b/src/renderers/docstring.ts new file mode 100644 index 0000000..eccc19f --- /dev/null +++ b/src/renderers/docstring.ts @@ -0,0 +1,115 @@ +import { ReflectionKind } from 'typedoc'; + +/** + * Convert a TypeDoc serialized comment into a Google-style markdown docstring. + * + * TypeDoc stores comments as a list of display parts (text/code/inline-tag) + * plus blockTags (e.g. @example, @returns, @param). This function renders + * them back to a stable markdown string so both MkDocs and MCP consumers + * receive the same human-readable documentation. + */ +export function renderComment(comment: any): string { + if (!comment) return ''; + const lines: string[] = []; + + const summary = renderParts(comment.summary); + if (summary.trim()) lines.push(summary.trim()); + + for (const tag of comment.blockTags || []) { + const tagName = (tag.tag || '').replace(/^@/, ''); + const content = renderParts(tag.content).trim(); + if (!content) continue; + const heading = tagHeading(tagName); + lines.push(''); + lines.push(heading); + lines.push(''); + lines.push(content); + } + + return lines.join('\n').trim(); +} + +function tagHeading(tag: string): string { + switch (tag) { + case 'param': + return '**Parameters:**'; + case 'returns': + case 'return': + return '**Returns:**'; + case 'example': + return '**Example:**'; + case 'throws': + return '**Throws:**'; + case 'deprecated': + return '**Deprecated:**'; + case 'see': + return '**See also:**'; + case 'remarks': + return '**Remarks:**'; + default: + return '**@' + tag + ':**'; + } +} + +/** Render a list of TypeDoc comment display parts into markdown. */ +export function renderParts(parts: any[] | undefined): string { + if (!parts || !Array.isArray(parts)) return ''; + const out: string[] = []; + for (const part of parts) { + if (typeof part === 'string') { + out.push(part); + continue; + } + if (!part) continue; + out.push(part.text ?? ''); + } + return out.join(''); +} + +/** Map TypeDoc numerical ReflectionKind to doc-weaver DocKind. */ +export function mapKind(kind: number): string { + switch (kind) { + case ReflectionKind.Module: + return 'module'; + case ReflectionKind.Namespace: + return 'namespace'; + case ReflectionKind.Class: + return 'class'; + case ReflectionKind.Interface: + return 'interface'; + case ReflectionKind.Enum: + return 'enum'; + case ReflectionKind.EnumMember: + return 'enum-member'; + case ReflectionKind.TypeAlias: + return 'type-alias'; + case ReflectionKind.Variable: + return 'variable'; + case ReflectionKind.Function: + return 'function'; + case ReflectionKind.Method: + return 'method'; + case ReflectionKind.Accessor: + return 'accessor'; + case ReflectionKind.Property: + return 'property'; + case ReflectionKind.Constructor: + return 'constructor'; + case ReflectionKind.CallSignature: + case ReflectionKind.ConstructorSignature: + return 'signature'; + case ReflectionKind.Parameter: + return 'parameter'; + default: + return 'unknown'; + } +} + +/** Classify a symbol as a React component or hook where possible. */ +export function classifyReact(name: string, kind: string): string { + if (kind === 'function' || kind === 'variable') { + if (/^use[A-Z0-9]/.test(name)) return 'hook'; + if (/^[A-Z]/.test(name)) return 'component'; + } + return kind; +} diff --git a/src/renderers/human-docs.ts b/src/renderers/human-docs.ts new file mode 100644 index 0000000..46644ee --- /dev/null +++ b/src/renderers/human-docs.ts @@ -0,0 +1,50 @@ +import { Application, TSConfigReader } from 'typedoc'; +import * as path from 'path'; + +/** + * Generate the human-facing documentation site using TypeDoc. + * + * TypeDoc plays the same role for doc-weaver that MkDocs plays for + * doc-forge: it renders the compiled model into a browsable site. The + * machine-facing output (MCP JSON) is produced separately. + */ +export interface HumanDocsOptions { + entryPoints: string[]; + tsconfig?: string; + name?: string; + out: string; + /** Optional path to a typedoc.json config. */ + options?: string; + /** Optional TypeDoc plugins (e.g. typedoc-plugin-markdown). */ + plugin?: string[]; + /** Skip TypeScript error checking. Defaults to true. */ + skipErrorChecking?: boolean; + /** TypeDoc entry-point strategy. Defaults to 'resolve'. */ + entryPointStrategy?: 'resolve' | 'expand' | 'merge' | 'packages'; +} + +export async function generateHumanDocs(opts: HumanDocsOptions): Promise { + const basePath = opts.tsconfig ? path.dirname(opts.tsconfig) : opts.entryPoints[0]; + const app = await Application.bootstrapWithPlugins( + { + entryPoints: opts.entryPoints, + tsconfig: opts.tsconfig, + name: opts.name, + out: opts.out, + options: opts.options, + plugin: opts.plugin, + basePath, + readme: 'none', + entryPointStrategy: opts.entryPointStrategy ?? 'resolve', + skipErrorChecking: opts.skipErrorChecking ?? true + }, + [new TSConfigReader()] + ); + + const project = await app.convert(); + if (!project) { + throw new Error('TypeDoc failed to convert the project'); + } + await app.generateDocs(project, opts.out); + return path.resolve(opts.out); +} diff --git a/src/renderers/mcp-renderer.ts b/src/renderers/mcp-renderer.ts new file mode 100644 index 0000000..e94a896 --- /dev/null +++ b/src/renderers/mcp-renderer.ts @@ -0,0 +1,113 @@ +import { + RefKind, + reflectionKindName, + resolveModulePath, + collectModules +} from '../models/typedoc.js'; +import { renderComment, mapKind, classifyReact } from './docstring.js'; +import { bestSignature } from './signature.js'; + +export interface McpBundle { + index: Record; + nav: Array<{ module: string; resource: string }>; + modules: Record }>; +} + +/** + * Transform a TypeDoc serialized reflection JSON into a doc-forge-compatible + * MCP bundle. + * + * The produced shape matches doc-forge exactly: + * + * - `index.json` -> `{ project, type, modules_count, source }` + * - `nav.json` -> `[ { module, resource } ]` with `docs://modules/{module}` + * - `modules/*.json` -> `{ module, content: { path, docstring, objects } }` + * + * Objects are rendered recursively with + * `{ name, kind, path, signature, docstring, members? }`. + */ +export class TypeDocJsonToMCP { + constructor( + private readonly project: any, + private readonly projectName?: string + ) {} + + transform(): McpBundle { + const name = this.projectName || this.project?.name || 'project'; + const moduleRefs = collectModules(this.project); + + const nav: McpBundle['nav'] = []; + const modules: McpBundle['modules'] = {}; + + for (const mod of moduleRefs) { + const modPath = resolveModulePath(mod, this.project); + nav.push({ module: modPath, resource: `docs://modules/${modPath}` }); + modules[modPath] = { + module: modPath, + content: this.renderModule(mod, modPath) + }; + } + + const index = { + project: name, + type: 'docforge-model', + modules_count: nav.length, + source: 'doc-weaver' + }; + + return { index, nav, modules }; + } + + private renderModule(mod: any, modPath: string): Record { + const objects: Record = {}; + for (const child of mod.children || []) { + if (child.kind === RefKind.Module) continue; + objects[child.name] = this.renderObject(child, modPath); + } + return { + path: modPath, + docstring: renderComment(mod.comment), + objects + }; + } + + private renderObject(ref: any, parentPath: string): Record { + const kindName = classifyReact(ref.name, mapKind(ref.kind)); + const path = `${parentPath}.${ref.name}`; + + const data: Record = { + name: ref.name, + kind: kindName, + path, + signature: bestSignature(ref), + docstring: renderComment(ref.comment) + }; + + // Prefer an explicit @kind tag when present (mirrors doc-forge kinds). + const kindTag = findBlockTag(ref.comment, '@kind'); + if (kindTag) data.kind = kindTag; + + const members = this.collectMembers(ref, path); + if (Object.keys(members).length) data.members = members; + + return data; + } + + private collectMembers(ref: any, parentPath: string): Record { + const members: Record = {}; + for (const child of ref.children || []) { + members[child.name] = this.renderObject(child, parentPath); + } + // Enum members and similar leaf children are already handled by children. + return members; + } +} + +function findBlockTag(comment: any, tag: string): string | undefined { + for (const bt of comment?.blockTags || []) { + if (bt.tag === tag) { + return (bt.content || []).map((p: any) => p.text ?? '').join('').trim(); + } + } + return undefined; +} diff --git a/src/renderers/signature.ts b/src/renderers/signature.ts new file mode 100644 index 0000000..878a645 --- /dev/null +++ b/src/renderers/signature.ts @@ -0,0 +1,90 @@ +/** Utilities for reconstructing human-readable signatures from TypeDoc JSON. */ + +/** Render a TypeDoc type object to a compact TypeScript-like string. */ +export function renderType(type: any): string { + if (!type) return 'unknown'; + if (typeof type === 'string') return type; + + switch (type.type) { + case 'intrinsic': + return type.name ?? 'unknown'; + case 'reference': { + const name = type.name ?? 'unknown'; + const args = (type.typeArguments || []).map(renderType); + const qualified = + type.qualifiedName && type.qualifiedName !== name ? type.qualifiedName : name; + return args.length ? `${qualified}<${args.join(', ')}>` : qualified; + } + case 'array': + return `${renderType(type.elementType)}[]`; + case 'union': + return (type.types || []).map(renderType).join(' | '); + case 'intersection': + return (type.types || []).map(renderType).join(' & '); + case 'tuple': + return `[${(type.elements || []).map(renderType).join(', ')}]`; + case 'literal': + return type.value === null ? 'null' : JSON.stringify(type.value); + case 'reflection': + if (type.declaration?.signatures?.length) { + const sig = type.declaration.signatures[0]; + return sig.type ? renderType(sig.type) : 'function'; + } + if (type.declaration?.children?.length) { + const members = type.declaration.children + .map((c: any) => `${c.name}: ${renderType(c.type)}`) + .join('; '); + return `{ ${members} }`; + } + return 'object'; + case 'typeParameter': + return type.name ?? 'T'; + case 'predicate': + return type.name ?? 'boolean'; + case 'query': + return `typeof ${renderType(type.queryType)}`; + case 'rest': + return `...${renderType(type.elementType)}`; + case 'namedTupleMember': + return `${type.name}: ${renderType(type.elementType)}`; + case 'optional': + return `${renderType(type.elementType)}?`; + default: + return type.name ?? 'unknown'; + } +} + +/** Render a function/call signature to a TypeScript-like string. */ +export function renderSignature(signature: any, fallbackName?: string): string { + if (!signature) return fallbackName ?? ''; + const name = signature.name ?? fallbackName ?? ''; + const params = (signature.parameters || []) + .map((p: any) => { + const optional = p.flags?.isOptional ? '?' : ''; + return `${p.name}${optional}: ${renderType(p.type)}`; + }) + .join(', '); + const returns = signature.type ? `: ${renderType(signature.type)}` : ''; + return `${name}(${params})${returns}`; +} + +/** + * Choose the best signature string for a reflection. + * + * Functions/methods/constructors keep their declared signatures; variables + * and properties expose their type; classes fall back to their name. + */ +export function bestSignature(ref: any): string { + if (ref.signatures?.length) { + return ref.signatures.map((s: any) => renderSignature(s, ref.name)).join(' | '); + } + if (ref.getSignature || ref.setSignature) { + const get = ref.getSignature ? renderSignature(ref.getSignature, ref.name) : ''; + const set = ref.setSignature ? `set ${renderSignature(ref.setSignature, ref.name)}` : ''; + return [get, set].filter(Boolean).join(' | '); + } + if (ref.type) { + return `${ref.name}: ${renderType(ref.type)}`; + } + return ref.name ?? ''; +} diff --git a/src/servers/mcp-server.ts b/src/servers/mcp-server.ts new file mode 100644 index 0000000..eaaa57f --- /dev/null +++ b/src/servers/mcp-server.ts @@ -0,0 +1,82 @@ +import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; +import * as path from 'path'; +import fs from 'fs-extra'; + +/** + * MCP server serving a pre-generated doc-weaver bundle. + * + * Mirrors `docforge.servers.MCPServer`: it exposes read-only documentation + * resources and a single health-check tool. + * + * Resources: + * - `docs://index` - project metadata + * - `docs://nav` - navigation structure + * - `docs://modules/{module}` - individual module documentation + */ +export class MCPServer { + private readonly server: McpServer; + + constructor( + private readonly mcpRoot: string, + name: string + ) { + this.server = new McpServer({ name, version: '0.1.0' }); + this.registerResources(); + this.registerTools(); + } + + private readJson( + relPath: string, + uri: string + ): { contents: Array<{ uri: string; mimeType?: string; text: string }> } { + const full = path.join(this.mcpRoot, relPath); + let text: string; + if (fs.existsSync(full)) { + text = fs.readFileSync(full, 'utf-8'); + } else { + text = JSON.stringify({ error: 'not_found', path: full }, null, 2); + } + return { contents: [{ uri, mimeType: 'application/json', text }] }; + } + + private registerResources(): void { + this.server.registerResource( + 'index', + 'docs://index', + { title: 'Index', mimeType: 'application/json' }, + async (uri) => this.readJson('index.json', uri.href) + ); + + this.server.registerResource( + 'nav', + 'docs://nav', + { title: 'Navigation', mimeType: 'application/json' }, + async (uri) => this.readJson('nav.json', uri.href) + ); + + const template = new ResourceTemplate('docs://modules/{module}', { list: undefined }); + this.server.registerResource( + 'module', + template, + { title: 'Module', mimeType: 'application/json' }, + async (uri, variables) => { + const module = String(variables.module); + return this.readJson(path.join('modules', module + '.json'), uri.href); + } + ); + } + + private registerTools(): void { + this.server.registerTool( + 'ping', + { description: 'Health check that returns "pong".' }, + async () => ({ content: [{ type: 'text', text: 'pong' }] }) + ); + } + + async run(): Promise { + const transport = new StdioServerTransport(); + await this.server.connect(transport); + } +} diff --git a/templates/typedoc.json b/templates/typedoc.json new file mode 100644 index 0000000..637fb03 --- /dev/null +++ b/templates/typedoc.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "entryPointStrategy": "resolve", + "entryPoints": ["index.ts"], + "readme": "none", + "excludePrivate": false, + "excludeInternal": false, + "categorizeByGroup": true, + "navigation": { + "includeCategories": true, + "includeGroups": true + }, + "plugin": [], + "commentStyle": "jsdoc", + "blockTags": [ + "@defaultValue", + "@deprecated", + "@example", + "@param", + "@remarks", + "@returns", + "@see", + "@throws" + ], + "inlineTags": ["@link", "@linkcode", "@linkplain"], + "validation": { + "notExported": false, + "invalidLink": true, + "notDocumented": false + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c77d8c7 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "outDir": "dist", + "rootDir": "src", + "resolveJsonModule": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "templates"] +}