Skip to content

Add OpenAPI docs generation#123

Merged
KayleeWilliams merged 12 commits into
mainfrom
feat/openapi-docs-generation
Jul 6, 2026
Merged

Add OpenAPI docs generation#123
KayleeWilliams merged 12 commits into
mainfrom
feat/openapi-docs-generation

Conversation

@KayleeWilliams

@KayleeWilliams KayleeWilliams commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

Native OpenAPI 3.x → API reference page generation, built for great docs DX and agent-friendliness. One openapi block in docs.config.ts turns a spec into MDX operation pages that render through your docs UI and flatten into agent-readable markdown — flowing into llms.txt, search, markdown mirrors, AGENTS.md, and Agent Readability artifacts.

What each generated page carries

  • Machine-scannable frontmatter: type: api-reference, method, path, operationId, server, apiVersion, tags, canonicalUrl (when baseUrl is set), lastModified (spec's git commit date), and source pointing at the spec itself — serialized through the shared frontmatter YAML serializer
  • Full contract: parameter/property tables with nested dotted rows (results[].title), the dereferenced JSON Schema per media type (includeSchemas: false to opt out), named or schema-synthesized JSON examples, and response headers. Operation-level parameters override path-item parameters per the OpenAPI spec
  • Real code samples: cURL + fetch with example bodies, required query params, path parameters substituted from spec examples (/pages/{urlPath}/pages/docs/quickstart), and auth headers derived from the security scheme; Redocly-style x-codeSamples override
  • Navigation: a generated overview page per spec (operations grouped by tag) joins the nav as the section landing page; operation pages link back via a Related section
  • MDX safety: operation prose is CommonMark per the OpenAPI spec — {/</} are escaped (code spans, fences, and autolinks preserved) so arbitrary specs can't break the build

Integration shapes (works everywhere)

  1. createDocsSource({ openapi }) / fumadocsSource({ openapi }) — generated pages live in a small temp overlay; authored docs are read live from contentDir (never copied or frozen). Sources expose cleanup(), with a best-effort process-exit sweep as fallback. Nav for generated pages resolves through resolveDocsNavigation's new extraDocsDirs option
  2. leadtype generate — picks up the config automatically (next/nuxt/sveltekit examples)
  3. Static-glob bundlers — writeOpenApiPages() into an app-local dir (TanStack Start / Vite recipe)
  4. stageOpenApiDocs() — the full-copy staging primitive for custom pipelines

Rendering follows the existing component naming contract: seven Api* tags, prop types from leadtype/mdx, built-in markdown flatteners registered in the native dispatcher, and copyable reference implementations in the Fumadocs and TanStack examples (both browser-verified). The dependency-free leadtype/mdx/openapi subpath exports flattenApiSchemaRows() so custom renderers derive the same nested property rows as the markdown mirrors — one flattener, no drift, safe to import from client components.

DX guardrails

  • Missing local specs fail loud: the error names the resolved absolute path, the base directory, and the openapiCwd/cwd escape hatches (and notes that the CLI resolves relative inputs from the config file directory)
  • Slug collisions across specs get numeric suffixes — including overview pages when specs share an output dir, with nav/canonical/Related links following the real slug; generated pages colliding with authored routes or pre-existing files throw descriptive errors instead of overwriting
  • Spec-derived strings are encoded for JSX attribute safety (&/"), closing the attribute-injection path alongside the existing prose escaping
  • OpenAPI-only configs (product + openapi) validate without dummy nav; remote spec/$ref fetches time out after 30s; --include/--exclude runs skip OpenAPI generation with a warning; the spec trust model is documented

Dogfooding

docs/openapi/leadtype-api.yaml (two endpoints, enums, formats, named examples, response headers, error schemas) generates into the package docs bundle, the Fumadocs example, and the TanStack example.

Docs

  • docs/reference/openapi.mdx — full reference: quickstart, page anatomy, renderer wiring per integration shape (including flattenApiSchemaRows), options table, library API
  • docs/writing/components.mdxApi* tags added to the naming contract
  • docs/changelog/0-4.mdx — release notes entry

Checks

  • bun run --filter leadtype check-types / lint / test / build (601 tests via the pre-commit bun test run)
  • bun x tsgo --noEmit in apps/tanstack and apps/fumadocs-example
  • leadtype lint docs --error-unknown --max-warnings 0 (CI invocation)
  • Browser-verified in both example apps: rendered operation pages, overview page, tabbed code samples with substituted path params, nested results[].title rows, collapsible JSON Schema, sidebar/TOC, .md content negotiation, and clean dev-server output (no client-bundle errors)

Review

A multi-agent review pass (8 finder angles, adversarially verified) confirmed and fixed 7 findings before merge: inverted parameter precedence, literal {param} placeholders in code samples, a temp-directory leak plus frozen-snapshot dev semantics in createDocsSource (replaced with the overlay design), a triplicated schema-row flattener (now one shared helper), divergent relative-path resolution between the CLI and source APIs (now fail-loud with guidance), and hand-rolled frontmatter YAML (now the shared serializer).

A second round addressed all open bot-review findings (Pullfrog, Codex, CodeRabbit): the JSX attribute-injection vector in mdxProp, the multi-spec shared-output overview drop, OpenAPI-only config validation, remote-fetch timeouts, failure-path staging cleanup (plus the llm-generate try/finally), fail-loud overwrite protection, React key collisions in the example renderers, filtered-generate semantics, and the documented $ref trust model.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds native OpenAPI 3.x API reference generation, including MDX page creation, markdown flattening for Api* components, source/CLI staging, app rendering support, and documentation/config updates.

Changes

OpenAPI Reference Generation

Layer / File(s) Summary
OpenAPI generation engine
packages/leadtype/src/openapi/index.ts, packages/leadtype/src/openapi/openapi.test.ts
Defines OpenAPI models, spec loading/dereferencing, normalization, MDX/page rendering, navigation, disk staging, and the related test coverage.
Markdown flattening for Api* components
packages/leadtype/src/mdx/openapi-schema-rows.ts, packages/leadtype/src/markdown/plugins/openapi.ts, packages/leadtype/src/markdown/component-dispatcher.ts, packages/leadtype/src/markdown/default-transforms.ts, packages/leadtype/src/mdx/tag-types.ts, apps/tanstack/src/components/docs-mdx/api.tsx, apps/fumadocs-example/lib/mdx-components.tsx, apps/tanstack/src/components/docs-mdx/mdx-components.ts
Adds schema-row flattening and the OpenAPI MDX-to-markdown transformer, then registers Api* component handling in the dispatcher, default transform pipeline, and app MDX renderers.
Public package surface
packages/leadtype/src/mdx/index.ts, packages/leadtype/src/index.ts, packages/leadtype/src/llm/llm.ts, packages/leadtype/package.json, packages/leadtype/rollup.config.ts, packages/leadtype/src/internal/package-surface.test.ts, packages/leadtype/src/fumadocs/index.ts, packages/leadtype/src/framework-adapters.test.ts
Exports OpenAPI types, tag contracts, subpaths, config fields, build entries, and cleanup-enabled source types across the package surface.
Docs-source and CLI staging integration
packages/leadtype/src/source/index.ts, packages/leadtype/src/cli/generate.ts, packages/leadtype/scripts/generate-docs.ts, packages/leadtype/src/llm/llm.ts, packages/leadtype/src/source/source.test.ts, packages/leadtype/src/cli.test.ts, apps/fumadocs-example/lib/source.ts, apps/tanstack/scripts/docs-source-manifest.ts, apps/tanstack/scripts/llm-generate.ts, apps/tanstack/scripts/mdx-convert.ts, apps/tanstack/src/routes/docs/$.tsx, apps/tanstack/src/styles.css
Adds OpenAPI-aware staging, config validation, merged navigation, cleanup, route lookup, and multi-root docs resolution across source, CLI, and app build flows.
Documentation and sample spec
docs/docs.config.ts, docs/openapi/leadtype-api.yaml, docs/reference/openapi.mdx, docs/changelog/0-4.mdx, docs/writing/components.mdx, .changeset/openapi-generated-docs.md, .gitignore
Adds the OpenAPI example spec, docs configuration, generated-reference documentation, changelog entry, component naming updates, release note, and ignore rules for generated artifacts.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DocsConfig as docs.docs.config.ts
  participant OpenApi as packages/leadtype/src/openapi/index.ts
  participant Source as packages/leadtype/src/source/index.ts
  participant CLI as packages/leadtype/src/cli/generate.ts
  participant AppRoute as apps/tanstack/src/routes/docs/$.tsx

  DocsConfig->>OpenApi: normalizeOpenApiConfig()
  OpenApi->>OpenApi: load spec, dereference $refs, normalize pages
  Source->>OpenApi: stageOpenApiDocs()
  OpenApi-->>Source: temp overlay pages + nav
  CLI->>OpenApi: writeOpenApiPages()
  AppRoute->>AppRoute: merge authored and generated MDX modules
Loading

Possibly related PRs

  • inthhq/leadtype#35: Both PRs extend the same docs-source and fumadocs integration surface, and this PR builds on that path to add OpenAPI overlay generation and cleanup.
  • inthhq/leadtype#113: Both PRs modify the markdown component dispatch/flattening pipeline, with this PR adding Api* OpenAPI handlers and transforms.
  • inthhq/leadtype#118: Both PRs touch the shared markdown transform registration path in default-transforms.ts, and this PR adds OpenAPI flattener wiring on top of it.

Poem

I hopped through specs with a nose so keen,
Found MDX pages in a docs machine.
With Api* badges, tables, and cheer,
This bunny says “OpenAPI is here!” 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly states the primary change: adding OpenAPI docs generation.
Description check ✅ Passed The description accurately summarizes the OpenAPI docs generation feature and its integrations.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/openapi-docs-generation

Comment @coderabbitai help to get the list of available commands.

- Register Api* handlers in the native markdown dispatcher so generated
  pages flatten into agent-readable markdown (llms.txt, search, bundles)
- Escape MDX-significant syntax in operation prose so CommonMark
  descriptions ({id}, <token>) cannot break the docs build
- Summarize nested schemas (array items, allOf merge) and render dotted
  property rows (results[].title) in both renderers
- Generate real code samples: auth headers from security schemes,
  synthesized JSON bodies, required query params, x-codeSamples support
- Render named examples and synthesize request/response examples from
  schemas for JSON media types
- Drop the duplicate body h1; frontmatter description uses the summary
- Validate openapi config in the CLI with descriptive errors
- Add stageOpenApiDocs() and accept openapi config in createDocsSource /
  fumadocsSource; simplify the package build and fumadocs example
- Share slug collision state across specs, allow webhooks-only 3.1 docs,
  reject Swagger 2.0 with a conversion hint
- Expand tests: flatten round-trip, MDX-unsafe prose, external/circular
  refs, tag filters, slug collisions, code samples, staging, validation
- Frontmatter description prefers the description's first paragraph over
  the summary (which usually mirrors the title), capped at 250 chars
- Render server URL and operation ID as inline code in flattened
  markdown so URLs avoid escaping artifacts
The example's docs:generate output (sitemap, robots.txt, mcp.json, feeds,
schema-map) was not covered by .gitignore and slipped into the previous
commit. Remove it from tracking and ignore the artifact paths.
- Add Api* renderer components (data-attribute styling, tabbed code
  samples via the app's Tabs) and register them in the MDX map
- Write generated API pages into src/generated/openapi-docs so Vite's
  static import.meta.glob can compile them; merge a second glob in the
  docs catch-all route and append manifest entries with matching globKeys
- Flatten the generated pages into public/docs mirrors (pipeline:convert)
  so search and agent artifacts include them
- Stage OpenAPI docs in pipeline:llm so llms.txt, docs-nav.json, and the
  agent-readability manifest carry the generated section; skills keep
  resolving from the real repo root
- Skip the body description on generated pages when it matches the
  frontmatter description (docs UIs already print it under the title)
Closes the fidelity gap against Vercel-style REST API markdown:

- Ship the dereferenced JSON Schema for request/response bodies (full
  enums, formats, constraints) in flattened markdown and as collapsible
  blocks in renderers; disable with includeSchemas: false
- Machine-scannable frontmatter on every generated page: type, method,
  path, operationId, server, apiVersion, tags, deprecated
- Generate an overview index page per source listing operations grouped
  by tag, wired into nav as the section landing page; operation pages
  gain a Related section linking the overview (and remote spec URLs);
  links use the new urlPrefix option (default /docs)
- Render response headers tables in the fumadocs and tanstack renderers
  (the flattener already did)
- Label examples and skip body descriptions that merely repeat the
  frontmatter (whitespace-insensitive)
- Enrich the dogfood spec: second endpoint, $ref schemas, enums,
  formats, defaults, named examples, response headers, 401/404 errors
- New baseUrl option (per-source or via normalizeOpenApiConfig defaults)
  adds canonicalUrl frontmatter to operation and overview pages; the CLI
  forwards --base-url and createDocsSource forwards its own baseUrl
- lastModified comes from the spec file's latest git commit date,
  falling back to file mtime; remote specs omit it rather than faking
  freshness from fetch time
- Thread baseUrl through stageOpenApiDocs and the TanStack pipeline
  scripts
source now carries the spec path or URL the page was generated from
(agents can fetch the machine-readable contract directly) instead of the
literal "openapi"; drop the redundant generated flag — type:
api-reference already marks these pages.
- Reference page: generated frontmatter example, static-glob bundler
  recipe (TanStack Start / Vite), overview page output, and both example
  component maps
- Components naming contract: add the Api* tags with a pointer to the
  OpenAPI reference
- Changelog 0.4: OpenAPI API reference generation section
@KayleeWilliams KayleeWilliams marked this pull request as ready for review July 2, 2026 16:20

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 10d54dbdab

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/leadtype/src/cli/generate.ts Outdated
Comment on lines +1305 to +1308
const openapi = validateDocsOpenApiConfig(
value.openapi,
`docs config at "${configPath}"`
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Allow OpenAPI-only docs configs

When a config contains only product + openapi—the quickstart shape documented in docs/reference/openapi.mdxvalidateDocsConfig throws at the existing groups/navigation check before this new OpenAPI validation runs. That makes leadtype generate unusable for OpenAPI-only docs unless users add dummy groups or navigation, even though the generated OpenAPI nav is appended later.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 904597evalidateDocsConfig now accepts configs with openapi set and no groups/navigation (the documented quickstart shape); product-only configs without openapi still fail as before. Regression tests added in cli.test.ts.

Comment thread packages/leadtype/src/source/index.ts Outdated
Comment on lines +408 to +415
const staged = await stageOpenApiDocs({
contentDir: sourceContentDir,
cwd: config.openapiCwd,
openapi: config.openapi,
...(config.baseUrl ? { baseUrl: config.baseUrl } : {}),
});
contentDir = staged.contentDir;
nav = [...(config.nav ?? []), ...staged.nav];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep a cleanup path for staged OpenAPI sources

When createDocsSource({ openapi }) is used, stageOpenApiDocs creates a full temp copy and returns a cleanup callback, but this block only keeps contentDir and nav. Since the returned DocsSource exposes no cleanup method, consumers such as fumadocsSource in dev/server processes cannot remove /tmp/leadtype-openapi-*, so every construction or hot reload leaks the staged docs tree.

Useful? React with 👍 / 👎.

Comment on lines +1221 to +1224
const allParameters = [
...pathParameters,
...(Array.isArray(operation.parameters) ? operation.parameters : []),
];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Let operation parameters override path parameters

For a path that defines a parameter and an operation redeclares the same name/in with a narrower schema, example, or description, OpenAPI treats the operation parameter as the override. This processes pathParameters first and then drops duplicates, so generated parameter tables and code samples keep the path-level definition and ignore the operation-specific one.

Useful? React with 👍 / 👎.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

This PR generates MDX component attributes from spec-derived strings using JS/JSON string escaping, which JSX/MDX attribute strings do not honor. A " in an untrusted spec's operationId, path, or serverUrl breaks out of the attribute and can inject arbitrary JSX/expressions into the compiled page — build-time code execution. This directly contradicts the PR's "arbitrary specs can't break the build" guarantee. See the inline comment on mdxProp.

Reviewed changes — a new native OpenAPI 3.x → MDX API-reference generator wired into the docs pipeline, example apps, and downstream agent artifacts.

  • Core generatorpackages/leadtype/src/openapi/index.ts (new, ~2004 lines): YAML load, cycle-guarded $ref dereferencing (incl. external file/URL refs), schema summarization (allOf/oneOf/anyOf), example synthesis, curl/fetch code samples, slug/collision handling, and MDX/nav/frontmatter rendering.
  • Flattenerspackages/leadtype/src/markdown/plugins/openapi.ts (new): markdown flatteners for the seven Api* components, reading attrs via getAttributeValue + JSON5.parse.
  • Source integrationcreateDocsSource/fumadocsSource gain openapi/openapiCwd, staging a temp copy of the docs dir and appending generated nav.
  • Pipeline & contract wiring — CLI generate forces staging when openapi is set; the seven Api* names are registered consistently across tag-types, dispatcher, default-transforms, and both example apps; new leadtype/openapi subpath export.
  • Dogfood + docsdocs/openapi/leadtype-api.yaml plus reference/changelog/components docs.

ℹ️ $ref resolution is an unrestricted local-file-read and outbound-fetch primitive

External $refs trigger fetch() (any URL) and readFile() (any resolvable path, including ../../.. traversal), with no allowlist, no root confinement, no timeout, and no size cap. A resolved local file that happens to YAML-parse into an object is dereferenced into the operation's schema and rendered into the public docs page.

  • For a build-time tool driven by a trusted docs-config author this matches how swagger-parser / Redocly behave and is likely acceptable.
  • This PR is the first introduction of both fetch() and arbitrary-file-read into this package, so the trust boundary is worth an explicit decision rather than an implicit one.
Technical details
# `$ref` resolution has no confinement

## Affected sites
- `packages/leadtype/src/openapi/index.ts` `readInput` (~L428-454) — `fetch(input)` on any `https?://`; `readFile(path.resolve(cwd, input))` on any local path.
- `packages/leadtype/src/openapi/index.ts` `loadExternalRef` (~L456-492) — resolves `$ref` targets relative to the referring doc with no jail.

## Required outcome
- A documented, intentional trust boundary for spec sources.

## Open questions for the human
- Are ingested specs ever untrusted (e.g. a hosted-docs product accepting customer specs)? If so, this needs a root-jail for file refs, a host allowlist + timeout for URL refs, and a size cap. If specs are always first-party/trusted, a one-line note in `docs/reference/openapi.mdx` stating that is enough.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using Claude Opus𝕏

Comment thread packages/leadtype/src/openapi/index.ts Outdated

function mdxProp(name: string, value: unknown): string {
if (typeof value === "string") {
return `${name}=${JSON.stringify(value)}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 String props are serialized with JSON.stringify, which escapes an embedded " as \". JSX/MDX attribute strings do not honor backslash escapes — the attribute value ends at the first raw ", so the rest of a crafted string is parsed as new JSX attributes (including {…} expression containers). operation.path, operation.operationId, and operation.serverUrl flow straight from the spec into <ApiEndpoint> (renderOperationMdx, ~L1646) and none of them pass through escapeMarkdownForMdx. A spec containing a " in any of these can therefore break the build or inject arbitrary JSX/JS that executes at MDX-compile time. This breaks the PR's stated "arbitrary specs can't break the build" guarantee.

Technical details
# JSX attribute-string injection via `mdxProp`

## Affected sites
- `packages/leadtype/src/openapi/index.ts:1496-1503``mdxProp` string branch returns `` `${name}=${JSON.stringify(value)}` ``.
- Fed by `renderMdxComponent("ApiEndpoint", …)` at ~L1646-1652 with unsanitized `path` / `operationId` / `serverUrl`.

## Why it's exploitable
Example `operationId`:
```
readUser" onLoad={(() => globalThis.process.exit(1))()} x="
```
serializes to `operationId="readUser\" onLoad={…} x=\""`. The JSX parser closes the string at the raw `"` after `readUser\`, then reads `onLoad={…}` as a real attribute whose expression is evaluated when the page compiles/renders.

## Required outcome
- Spec-derived string attribute values must be encoded so no spec content can terminate the attribute or introduce new attributes/expressions.

## Suggested approach
- Add a JSX-attribute-safe encoder for the string branch: keep the `name="…"` shape but HTML-encode `"` (→ `&quot;`) — inside a double-quoted JSX attribute `"` is the only terminator, and both React and the mdast attribute readers decode the entity back to `"`.
- Do **NOT** switch string props to a `{JSON.stringify(value)}` expression container: the flatteners in `markdown/plugins/openapi.ts` read `method`/`path`/`operationId`/`serverUrl` as plain string attributes via `getAttributeValue`, which returns the raw expression text (with quotes) for expression-valued attributes and would break flattening.
- Add a test feeding a `"` (and `<`/`{`) through `operationId`/`serverUrl`.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 904597emdxProp string props now go through a JSX-attribute encoder (&&amp;, then "" → "), keeping the plain-string attribute shape the markdown flatteners rely on. Tests cover a quote in operationIdround-tripping through the flattener and</{/&inserverUrl`.

Comment thread packages/leadtype/src/openapi/index.ts Outdated
const relativePath = normalizeDocsPath(
path.posix.join(config.output, "index.mdx")
);
if (!usedPaths.has(relativePath.toLowerCase())) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ When two sources share an output directory (the default output is api for every source, so openapi: [a, b] with no explicit output hits this), the second source's overview page is silently dropped here — usedPaths already contains api/index.mdx from the first source, so the guard skips it. But buildGeneratedNav (~L1846) unconditionally emits pages: ["index"] under the same base, so the second source contributes a nav node pointing at a page that was never written for it, and its operations never appear in any overview.

Technical details
# Overview page dropped / nav dangles across specs sharing an output dir

## Affected sites
- `packages/leadtype/src/openapi/index.ts:1888-1906` — index page is created only when `usedPaths` doesn't already hold `<output>/index.mdx`; operation pages get collision suffixes but the index page does not.
- `packages/leadtype/src/openapi/index.ts:1823` and `:1846``buildGeneratedNav` always lists `"index"` and receives no `usedPaths`, so it can't know the index wasn't written.

## Required outcome
- Two specs sharing an output dir must not silently lose the second overview page, and no emitted nav node may reference a page that wasn't written.

## Suggested approach
- Either collision-suffix the index page like operation pages (and thread the resulting slug into the nav node), or require/validate distinct `output` per source, or merge multiple specs targeting one output into a single combined overview + nav node.

## Open questions for the human
- Is multiple specs → one output dir a supported configuration, or should it be rejected at config-validation time?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 904597e — overview pages now collision-suffix like operation pages (index-2.mdx, …) when specs share an output dir, and the actually-written overview slug is threaded through buildGeneratedNav, canonicalUrl, and each operation page's Related links, so every nav node points at a written page and each spec's operations link to their own overview. Covered by a two-specs-shared-output test.

Comment thread packages/leadtype/src/source/index.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/fumadocs-example/lib/mdx-components.tsx`:
- Around line 125-479: The API docs rendering in this file is duplicated with
the TanStack app, including flattenSchemaRows, formatApiSchemaType, JsonExample,
MediaType, MediaTypeExamples, and the Api* components, so changes to schema
handling must be made twice. Extract the shared schema/documentation logic into
the leadtype package next to the ApiSchemaProperty types, and have this file and
the TanStack implementation consume that shared helper/render layer while
keeping only app-specific styling differences here.
- Around line 259-266: The ApiAuth requirements list in the requirements.map
render can produce duplicate React keys because key={names.join("+") ||
"anonymous"} is not guaranteed unique for repeated or identical scope sets.
Update the li key generation in mdx-components.tsx to include the map index
alongside the existing names.join("+") / "anonymous" value so each rendered
requirement has a unique key even when multiple entries share the same scopes or
are anonymous.

In `@apps/tanstack/scripts/llm-generate.ts`:
- Around line 198-199: The cleanup in llm-generate.ts is only called after all
generation steps, so it can be skipped if generateLlmsTxt,
generateLLMFullContextFiles, generateAgentReadabilityArtifacts,
generateSkillArtifacts, or resolveDocsNavigation throws. Wrap the main
generation flow in a try/finally block in llm-generate.ts and move
staged?.cleanup() into the finally, mirroring the pattern used in
generate-docs.ts so the temp directory from stageOpenApiDocs is always released.

In `@apps/tanstack/src/components/docs-mdx/api.tsx`:
- Around line 24-124: The schema-flattening logic in SchemaTable is duplicated
from the fumadocs example implementation; extract the shared MAX_SCHEMA_DEPTH,
formatSchemaType, and flattenSchemaRows behavior into a common helper and have
this api.tsx component consume it instead of maintaining a second copy. Keep the
local SchemaTable markup and JsonExample rendering here, but reference the
shared helper for building rows so both apps stay in sync.
- Around line 213-217: The requirements list in the api.tsx renderer uses a
non-unique React key in the map callback, which can collide for repeated
Anonymous or identical label entries. Update the requirements.map callback to
include the loop index alongside the existing label when setting the key in the
list item, so each rendered requirement in this block gets a stable unique key.

In `@packages/leadtype/src/openapi/index.ts`:
- Around line 1979-2004: The temp staging directory created in stageOpenApiDocs
can be left behind when writeOpenApiPages throws before cleanup is returned.
Wrap the staging work in a try/finally around the mkdtemp, cp, and
writeOpenApiPages flow, and ensure rm(stagedRoot, { force: true, recursive: true
}) runs on any failure. Keep the existing returned cleanup for successful calls,
but make the failure path in stageOpenApiDocs always release the stagedRoot and
stagedContentDir resources.
- Around line 428-454: readInput() currently fetches HTTP(S) OpenAPI specs with
no timeout, so an unresponsive URL can hang docs generation. Update the remote
branch in readInput to use AbortSignal.timeout(...) with fetch(input), and
handle the resulting abort/error with a clear OpenAPI message; make sure
recursive $ref loads still go through the same timed readInput path.

In `@packages/leadtype/src/openapi/openapi.test.ts`:
- Around line 551-585: Add a failure-path test for stageOpenApiDocs in
openapi.test.ts: the current coverage only verifies the success case. Create a
case where writeOpenApiPages throws (for example with an invalid OpenAPI spec)
and assert that the temporary staged contentDir is cleaned up even when staging
fails. Use the stageOpenApiDocs and writeOpenApiPages symbols to locate the
relevant flow and verify cleanup behavior.

In `@packages/leadtype/src/source/index.ts`:
- Around line 395-424: createDocsSource currently stages OpenAPI content via
stageOpenApiDocs but never exposes the disposer, so temporary source directories
can leak across repeated builds. Add an optional cleanup() method to the
DocsSource returned by createDocsSource, wire it to the disposer from
stageOpenApiDocs, and make sure leadtype/fumadocs forwards and invokes that
cleanup when OpenAPI staging is used.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2c38b59d-c8f1-4a45-938a-63080bed6fda

📥 Commits

Reviewing files that changed from the base of the PR and between d569171 and 10d54db.

📒 Files selected for processing (31)
  • .changeset/openapi-generated-docs.md
  • .gitignore
  • apps/fumadocs-example/lib/mdx-components.tsx
  • apps/fumadocs-example/lib/source.ts
  • apps/tanstack/scripts/docs-source-manifest.ts
  • apps/tanstack/scripts/llm-generate.ts
  • apps/tanstack/scripts/mdx-convert.ts
  • apps/tanstack/src/components/docs-mdx/api.tsx
  • apps/tanstack/src/components/docs-mdx/mdx-components.ts
  • apps/tanstack/src/routes/docs/$.tsx
  • apps/tanstack/src/styles.css
  • docs/changelog/0-4.mdx
  • docs/docs.config.ts
  • docs/openapi/leadtype-api.yaml
  • docs/reference/openapi.mdx
  • docs/writing/components.mdx
  • packages/leadtype/package.json
  • packages/leadtype/rollup.config.ts
  • packages/leadtype/scripts/generate-docs.ts
  • packages/leadtype/src/cli/generate.ts
  • packages/leadtype/src/index.ts
  • packages/leadtype/src/internal/package-surface.test.ts
  • packages/leadtype/src/llm/llm.ts
  • packages/leadtype/src/markdown/component-dispatcher.ts
  • packages/leadtype/src/markdown/default-transforms.ts
  • packages/leadtype/src/markdown/plugins/openapi.ts
  • packages/leadtype/src/mdx/index.ts
  • packages/leadtype/src/mdx/tag-types.ts
  • packages/leadtype/src/openapi/index.ts
  • packages/leadtype/src/openapi/openapi.test.ts
  • packages/leadtype/src/source/index.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity
Prefer unknown over any when the type is genuinely unknown
Use const assertions (as const) for immutable values and literal types
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • packages/leadtype/rollup.config.ts
  • apps/tanstack/src/routes/docs/$.tsx
  • packages/leadtype/src/mdx/index.ts
  • packages/leadtype/src/internal/package-surface.test.ts
  • apps/tanstack/scripts/mdx-convert.ts
  • docs/docs.config.ts
  • apps/tanstack/src/components/docs-mdx/mdx-components.ts
  • apps/fumadocs-example/lib/source.ts
  • packages/leadtype/src/index.ts
  • packages/leadtype/src/llm/llm.ts
  • apps/tanstack/scripts/docs-source-manifest.ts
  • packages/leadtype/src/markdown/default-transforms.ts
  • apps/tanstack/scripts/llm-generate.ts
  • packages/leadtype/src/markdown/component-dispatcher.ts
  • packages/leadtype/src/mdx/tag-types.ts
  • packages/leadtype/src/source/index.ts
  • packages/leadtype/scripts/generate-docs.ts
  • apps/fumadocs-example/lib/mdx-components.tsx
  • apps/tanstack/src/components/docs-mdx/api.tsx
  • packages/leadtype/src/markdown/plugins/openapi.ts
  • packages/leadtype/src/cli/generate.ts
  • packages/leadtype/src/openapi/openapi.test.ts
  • packages/leadtype/src/openapi/index.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions
Prefer for...of loops over .forEach() and indexed for loops
Use optional chaining (?.) and nullish coalescing (??) for safer property access
Prefer template literals over string concatenation
Use destructuring for object and array assignments
Use const by default, let only when reassignment is needed, never var
Always await promises in async functions - don't forget to use the return value
Use async/await syntax instead of promise chains for better readability
Handle errors appropriately in async code with try-catch blocks
Don't use async functions as Promise executors
Remove console.log, debugger, and alert statements from production code
Throw Error objects with descriptive messages, not strings or other values
Use try-catch blocks meaningfully - don't catch errors just to rethrow them
Prefer early returns over nested conditionals for error cases
Extract complex conditions into well-named boolean variables
Use early returns to reduce nesting
Prefer simple conditionals over nested ternary operators
Don't use eval() or assign directly to document.cookie
Avoid spread syntax in accumulators within loops
Use top-level regex literals instead of creating them in loops
Prefer specific imports over namespace imports
Use descriptive names for functions, variables, and types for meaningful naming
Add comments for complex logic, but prefer self-documenting code

Files:

  • packages/leadtype/rollup.config.ts
  • apps/tanstack/src/routes/docs/$.tsx
  • packages/leadtype/src/mdx/index.ts
  • packages/leadtype/src/internal/package-surface.test.ts
  • apps/tanstack/scripts/mdx-convert.ts
  • docs/docs.config.ts
  • apps/tanstack/src/components/docs-mdx/mdx-components.ts
  • apps/fumadocs-example/lib/source.ts
  • packages/leadtype/src/index.ts
  • packages/leadtype/src/llm/llm.ts
  • apps/tanstack/scripts/docs-source-manifest.ts
  • packages/leadtype/src/markdown/default-transforms.ts
  • apps/tanstack/scripts/llm-generate.ts
  • packages/leadtype/src/markdown/component-dispatcher.ts
  • packages/leadtype/src/mdx/tag-types.ts
  • packages/leadtype/src/source/index.ts
  • packages/leadtype/scripts/generate-docs.ts
  • apps/fumadocs-example/lib/mdx-components.tsx
  • apps/tanstack/src/components/docs-mdx/api.tsx
  • packages/leadtype/src/markdown/plugins/openapi.ts
  • packages/leadtype/src/cli/generate.ts
  • packages/leadtype/src/openapi/openapi.test.ts
  • packages/leadtype/src/openapi/index.ts
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{jsx,tsx}: Use function components over class components in React
Call hooks at the top level only, never conditionally
Specify all dependencies in hook dependency arrays correctly
Use the key prop for elements in iterables (prefer unique IDs over array indices)
Nest children between opening and closing tags instead of passing as props
Don't define components inside other components
Avoid dangerouslySetInnerHTML unless absolutely necessary
Use proper image components (e.g., Next.js <Image>) over <img> tags
Use Next.js <Image> component for images
Use next/head or App Router metadata API for head elements in Next.js
Use Server Components for async data fetching instead of async Client Components in Next.js
Use ref as a prop instead of React.forwardRef in React 19+

Files:

  • apps/tanstack/src/routes/docs/$.tsx
  • apps/fumadocs-example/lib/mdx-components.tsx
  • apps/tanstack/src/components/docs-mdx/api.tsx
**/*.{jsx,tsx,html}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{jsx,tsx,html}: Use semantic HTML and ARIA attributes for accessibility: provide meaningful alt text for images, use proper heading hierarchy, add labels for form inputs, include keyboard event handlers alongside mouse events, use semantic elements instead of divs with roles
Add rel="noopener" when using target="_blank" on links

Files:

  • apps/tanstack/src/routes/docs/$.tsx
  • apps/fumadocs-example/lib/mdx-components.tsx
  • apps/tanstack/src/components/docs-mdx/api.tsx
**/index.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Avoid barrel files (index files that re-export everything)

Files:

  • packages/leadtype/src/mdx/index.ts
  • packages/leadtype/src/index.ts
  • packages/leadtype/src/source/index.ts
  • packages/leadtype/src/openapi/index.ts
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{test,spec}.{js,ts,jsx,tsx}: Write assertions inside it() or test() blocks
Avoid done callbacks in async tests - use async/await instead
Don't use .only or .skip in committed code
Keep test suites reasonably flat - avoid excessive describe nesting

Files:

  • packages/leadtype/src/internal/package-surface.test.ts
  • packages/leadtype/src/openapi/openapi.test.ts
🧠 Learnings (1)
📚 Learning: 2026-06-09T18:30:08.038Z
Learnt from: KayleeWilliams
Repo: inthhq/leadtype PR: 97
File: .changeset/search-prototype-safety-and-scaling.md:5-5
Timestamp: 2026-06-09T18:30:08.038Z
Learning: In this repo, `.changeset/*.md` files must not start the body with an H1/first-line heading (`#`) immediately after the YAML frontmatter. The changesets tool inlines the body as bullet entries into `CHANGELOG.md` during release, and a leading `#` heading would break the generated changelog format. As a result, MD041 (`first-line-heading`) warnings for files under `.changeset/` are expected false positives and should be ignored.

Applied to files:

  • .changeset/openapi-generated-docs.md
🪛 ast-grep (0.44.0)
packages/leadtype/src/openapi/index.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🪛 Biome (2.5.1)
apps/tanstack/src/styles.css

[error] 672-672: Tailwind-specific syntax is disabled.

(parse)


[error] 676-676: Tailwind-specific syntax is disabled.

(parse)


[error] 680-680: Tailwind-specific syntax is disabled.

(parse)


[error] 717-717: Tailwind-specific syntax is disabled.

(parse)


[error] 721-721: Tailwind-specific syntax is disabled.

(parse)


[error] 725-725: Tailwind-specific syntax is disabled.

(parse)


[error] 729-729: Tailwind-specific syntax is disabled.

(parse)


[error] 733-733: Tailwind-specific syntax is disabled.

(parse)


[error] 738-738: Tailwind-specific syntax is disabled.

(parse)


[error] 742-742: Tailwind-specific syntax is disabled.

(parse)


[error] 746-746: Tailwind-specific syntax is disabled.

(parse)


[error] 750-750: Tailwind-specific syntax is disabled.

(parse)


[error] 754-754: Tailwind-specific syntax is disabled.

(parse)


[error] 758-758: Tailwind-specific syntax is disabled.

(parse)


[error] 762-762: Tailwind-specific syntax is disabled.

(parse)

🪛 Checkov (3.3.2)
docs/openapi/leadtype-api.yaml

[high] 1-196: Ensure that the global security field has rules defined

(CKV_OPENAPI_4)


[medium] 104-109: Ensure that arrays have a maximum number of items

(CKV_OPENAPI_21)

🪛 LanguageTool
docs/writing/components.mdx

[grammar] ~326-~326: Ensure spelling is correct
Context: ...ocs/reference/mdx) (ApiEndpointProps, ApiResponsesProps, …). tsx <ApiEndpoint method="get" path="/users/{id}" operationId="readUser" /> ### Mermaid Diagrams authored as plain text...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.22.1)
.changeset/openapi-generated-docs.md

[warning] 5-5: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

🔇 Additional comments (38)
.changeset/openapi-generated-docs.md (1)

1-9: LGTM!

docs/changelog/0-4.mdx (1)

12-19: LGTM!

docs/docs.config.ts (1)

27-34: LGTM!

Also applies to: 257-257

docs/openapi/leadtype-api.yaml (2)

133-159: 🎯 Functional Correctness | ⚡ Quick win

Path parameter contains a literal slash.

urlPath is declared as a plain path parameter (in: path, default style: simple) but its example (docs/quickstart) contains an embedded /. Standard path-param matching (and most routers) treats / as a segment delimiter, so a literal value like docs/quickstart won't match a single {urlPath} segment without style: simple; allowReserved: true (which most tooling still doesn't fully support) or restructuring the route as a wildcard/catch-all.

Since this is a dogfood/example spec used to demonstrate page generation, verify whether this is intentional illustrative text or should be corrected (e.g. .../pages/{urlPath+} convention, or documenting the required escaping).


1-195: LGTM!

docs/reference/openapi.mdx (1)

1-367: LGTM!

docs/writing/components.mdx (1)

23-27: LGTM!

Also applies to: 324-331

.gitignore (1)

44-49: 🎯 Functional Correctness

OpenAPI output is already ignored. apps/tanstack/.gitignore covers src/generated/, so src/generated/openapi-docs is already excluded; the root .gitignore only needs the public/ artifacts.

			> Likely an incorrect or invalid review comment.
apps/fumadocs-example/lib/mdx-components.tsx (2)

10-18: LGTM!


718-724: LGTM!

apps/fumadocs-example/lib/source.ts (1)

14-24: LGTM!

apps/tanstack/src/components/docs-mdx/api.tsx (1)

1-9: LGTM!

Also applies to: 164-366

apps/tanstack/src/components/docs-mdx/mdx-components.ts (1)

2-10: LGTM!

Also applies to: 28-34

apps/tanstack/src/routes/docs/$.tsx (1)

34-51: LGTM!

apps/tanstack/src/styles.css (1)

670-764: 🩺 Stability & Availability

@apply is already used elsewhere in apps/tanstack/src/styles.css, and there’s no CSS/Tailwind-specific Biome override in the repo, so this isn’t a block-specific lint issue.

			> Likely an incorrect or invalid review comment.
packages/leadtype/src/openapi/index.ts (2)

456-492: 🔒 Security & Privacy

Unbounded local/remote $ref resolution — confirm trust model.

loadExternalRef resolves relative local paths via path.resolve(baseDir, targetInput) with no containment check, and recursively fetches remote URLs referenced inside an already-loaded document with no allowlist. If any OpenAPI spec fed into this engine (directly or via a chain of $refs) can come from a less-trusted source (contributor-submitted spec, third-party partner spec fetched by URL, etc.), this allows arbitrary local file reads (path traversal, leaking file contents into public generated docs) and/or SSRF (fetching internal-only URLs from within $refs during doc generation).

Please confirm whether openapi.input/$ref targets are always fully trusted (e.g. only authored by the docs-site owner) in every integration point (CLI, createDocsSource, stageOpenApiDocs). If any less-trusted path exists, consider constraining $ref resolution to a configured root directory and/or an allowlist of remote hosts.


319-401: LGTM!

Also applies to: 494-534, 1460-1490

packages/leadtype/src/openapi/openapi.test.ts (1)

1-658: LGTM!

packages/leadtype/src/markdown/plugins/openapi.ts (2)

1-345: LGTM!


219-238: 🎯 Functional Correctness

Deprecated shorthand is already handled; no change needed.

			> Likely an incorrect or invalid review comment.
packages/leadtype/src/markdown/component-dispatcher.ts (1)

19-27: LGTM!

Also applies to: 86-92

packages/leadtype/src/markdown/default-transforms.ts (1)

25-25: LGTM!

Also applies to: 59-67, 95-95, 108-114

packages/leadtype/src/llm/llm.ts (1)

33-33: LGTM!

Also applies to: 477-481

packages/leadtype/src/mdx/tag-types.ts (1)

22-32: LGTM!

Also applies to: 149-190

packages/leadtype/src/mdx/index.ts (1)

55-63: LGTM!

packages/leadtype/src/index.ts (1)

7-7: LGTM!

Type re-exports match the OpenApi* types consumed by tag-types.ts, and the omission of runtime functions (stageOpenApiDocs, writeOpenApiPages, etc.) from the root export is consistent with downstream consumers importing them directly from leadtype/openapi.

Also applies to: 74-97

packages/leadtype/package.json (1)

70-73: LGTM!

packages/leadtype/rollup.config.ts (1)

18-18: LGTM!

packages/leadtype/src/internal/package-surface.test.ts (1)

91-94: LGTM!

packages/leadtype/src/source/index.ts (1)

146-156: LGTM!

Also applies to: 522-522

packages/leadtype/src/cli/generate.ts (3)

74-79: LGTM!

Also applies to: 309-309, 1305-1326, 1861-1861, 2338-2353


2630-2646: 🗄️ Data Integrity & Integration

Generated OpenAPI pages may silently overwrite authored files in the mirror.

sourceMirror.docsDir is first populated with copied authored source files (via copySourceFiles/copyFilteredSourceFiles), then writeOpenApiPages writes generated MDX into the same directory tree. Based on the writeOpenApiPages implementation (via ../openapi), collision handling (usedPaths) only guards against collisions among generated pages across multiple specs — it doesn't check whether a path already exists from an authored file before calling writeFile. If an authored doc happens to share a relative path with a generated operation page, it would be overwritten without warning.

Please confirm whether the underlying generateOpenApiPages/writeOpenApiPages (in packages/leadtype/src/openapi/index.ts) checks for pre-existing files at the target path, and if not, consider surfacing a clear error rather than a silent overwrite.


2630-2669: 🎯 Functional Correctness

OpenAPI reference generation bypasses --include/--exclude path filters.

writeOpenApiPages runs unconditionally whenever metadata.openapi is set, regardless of hasExplicitPathFilters — the generated pages land in sourceMirror.docsDir even when the mirror was built from a filtered subset of authored docs, and convertAllMdx will convert everything present in that directory. So a scoped/filtered leadtype generate --include "guides/**" run still gets the entire OpenAPI reference bolted on, even though effectiveNav correctly drops the OpenAPI nav in that case (Line 2667-2669) — leaving the pages present in output but orphaned from nav.

Is this intentional (OpenAPI reference is always "whole" regardless of filtering) or should OpenAPI page generation itself respect args.include/args.exclude?

packages/leadtype/scripts/generate-docs.ts (1)

15-15: LGTM!

Also applies to: 32-136

apps/tanstack/scripts/docs-source-manifest.ts (2)

16-46: LGTM!


67-108: 🗄️ Data Integrity & Integration

No collision check between authored and generated OpenAPI manifest entries.

Generated OpenAPI entries are pushed into manifest without checking whether their derived urlPath/slug already exists among the authored pages entries. If an authored doc and a generated operation page resolve to the same route, the runtime catch-all lookup (routes/docs/$.tsx, not in this file set) could get ambiguous/duplicate manifest entries for the same URL.

Worth confirming whether this is already prevented upstream (e.g. by convention that OpenAPI-generated paths always live under a distinct prefix) or should be validated here.

apps/tanstack/scripts/llm-generate.ts (1)

23-55: LGTM!

Also applies to: 93-101, 112-112, 126-128, 165-165

apps/tanstack/scripts/mdx-convert.ts (1)

18-27: LGTM!

Also applies to: 57-77

Comment thread apps/fumadocs-example/lib/mdx-components.tsx Outdated
Comment thread apps/fumadocs-example/lib/mdx-components.tsx Outdated
Comment thread apps/tanstack/scripts/llm-generate.ts Outdated
Comment thread apps/tanstack/src/components/docs-mdx/api.tsx Outdated
Comment thread apps/tanstack/src/components/docs-mdx/api.tsx Outdated
Comment thread packages/leadtype/src/openapi/index.ts
Comment thread packages/leadtype/src/openapi/index.ts
Comment thread packages/leadtype/src/openapi/openapi.test.ts
Comment thread packages/leadtype/src/source/index.ts
- Substitute path parameters in generated cURL/fetch samples
- Let operation-level parameters override path-item parameters
- Replace full-tree staging in createDocsSource with a temp overlay:
  authored docs read live, cleanup() + process-exit sweep, nav resolved
  via new resolveDocsNavigation extraDocsDirs option
- Share one schema-row flattener via dependency-free leadtype/mdx/openapi
- Make spec-not-found errors name the resolved path and cwd options
- Serialize generated frontmatter with stringifyFrontmatter

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The new commit cleanly fixes several findings and adds no new issues, but the two highest-severity findings from the prior review remain unaddressed and still block merge (both threads are still open below).

Reviewed changes — the single new commit 0c29fca ("Fix review findings in OpenAPI generation") on top of the prior-review head 10d54db.

  • Shared schema-row flattener — extracted flattenApiSchemaRows() into mdx/openapi-schema-rows.ts, exported via leadtype/mdx and the new dependency-free leadtype/mdx/openapi subpath; both example apps and the markdown dispatcher now consume it instead of triplicated copies.
  • Overlay redesign for createDocsSource/fumadocsSource — authored docs now read live from contentDir while generated pages are written to a mkdtemp overlay root; DocsSource exposes an idempotent cleanup() plus a single process.on("exit") sweep, and nav resolves overlay pages via resolveDocsNavigation({ extraDocsDirs }). This resolves the prior temp-dir-leak finding.
  • Parameter precedence — operation-level parameters now override path-item parameters in original order via a parametersByKey Map.
  • Code-sample path paramscodeSampleUrl substitutes spec example values into {param} placeholders, falling back to the literal placeholder when no sample exists.
  • Frontmatter serialization — switched from hand-rolled JSON.stringify scalars to the shared stringifyFrontmatter (yaml lib), which properly quotes/escapes; this closes the frontmatter injection surface.
  • Missing-spec error — now names the resolved path and the openapiCwd/cwd escape hatches. Tests added for all of the above.

🚨 Prior critical finding still open — MDX/JSX attribute injection in mdxProp

mdxProp (packages/leadtype/src/openapi/index.ts:1511-1519) still serializes string props as `${name}=${JSON.stringify(value)}`, and renderMdxComponent("ApiEndpoint", …) at :1655-1661 still feeds operation.path, operationId, and serverUrl through it unescaped. JSX attribute strings do not honor backslash escapes, so a " in any of these spec-derived values remains a build-time code-execution vector. The frontmatter path is now safe via stringifyFrontmatter, but this JSX-attribute path is not. Details remain in the open thread on mdxProp.

⚠️ Prior important finding still open — multi-spec overview page dropped + dangling nav

When two specs share an output directory (the default output is api for every source), generateOpenApiPages still skips the second source's index.mdx via the usedPaths guard (:1904-1922) while buildGeneratedNav still unconditionally emits pages: ["index"] under the same base (:1839-1846, :1862). The second overview is lost and its nav node points at the first spec's overview. Details remain in the open thread.

Pullfrog  | Fix it ➔View workflow run | Using Claude Opus𝕏

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/leadtype/src/openapi/index.ts (1)

1550-1596: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract shared base-frontmatter builder to reduce duplication.

renderFrontmatter and renderIndexMdx independently rebuild near-identical frontmatter scaffolding (title/description, conditional group, type: "api-reference", source, conditional canonicalUrl, conditional lastModified) before calling stringifyFrontmatter. Extracting a small buildBaseFrontmatter(config, { title, description, canonicalUrl, lastModified }) helper would remove the duplication and prevent the two call sites from drifting out of sync as fields are added.

♻️ Sketch
+function buildBaseFrontmatter(
+  config: ResolvedOpenApiSourceConfig,
+  fields: { title: string; description: string; canonicalUrl?: string; lastModified?: string }
+): Record<string, unknown> {
+  const frontmatter: Record<string, unknown> = {
+    title: fields.title,
+    description: fields.description,
+  };
+  if (config.group) frontmatter.group = config.group;
+  frontmatter.type = "api-reference";
+  frontmatter.source = config.input;
+  if (fields.canonicalUrl) frontmatter.canonicalUrl = fields.canonicalUrl;
+  if (fields.lastModified) frontmatter.lastModified = fields.lastModified;
+  return frontmatter;
+}

Also applies to: 1778-1796

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/leadtype/src/openapi/index.ts` around lines 1550 - 1596, Extract the
duplicated frontmatter assembly shared by renderFrontmatter and renderIndexMdx
into a small helper like buildBaseFrontmatter so both paths use the same base
fields. Have the helper populate the common title/description, conditional
group, type, source, canonicalUrl, and lastModified fields, then let
renderFrontmatter add operation-specific metadata such as method, path,
operationId, server, apiVersion, tags, and deprecated. This will keep
renderFrontmatter and renderIndexMdx aligned and prevent future drift.
♻️ Duplicate comments (5)
packages/leadtype/src/openapi/index.ts (2)

1995-2020: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Temp staging directory still leaks on generation failure.

stageOpenApiDocs still creates stagedRoot and calls writeOpenApiPages without a try/finally. Any failure during generation (malformed spec, unresolved $ref, network error) throws before cleanup is returned, permanently orphaning the staged temp directory. This was already flagged in a prior review and remains unaddressed in this revision.

🩹 Suggested fix
   const stagedRoot = await mkdtemp(path.join(tmpdir(), "leadtype-openapi-"));
   const stagedContentDir = path.join(stagedRoot, path.basename(sourceDir));
-  await cp(sourceDir, stagedContentDir, { recursive: true });
-  const result = await writeOpenApiPages({
-    configs: normalizeOpenApiConfig(config.openapi, config.cwd ?? sourceDir, {
-      ...(config.baseUrl ? { baseUrl: config.baseUrl } : {}),
-    }),
-    docsDir: stagedContentDir,
-  });
-  return {
-    cleanup: async () => {
-      await rm(stagedRoot, { force: true, recursive: true });
-    },
-    contentDir: stagedContentDir,
-    indexPages: result.indexPages,
-    nav: result.nav,
-    pages: result.pages,
-  };
+  try {
+    await cp(sourceDir, stagedContentDir, { recursive: true });
+    const result = await writeOpenApiPages({
+      configs: normalizeOpenApiConfig(config.openapi, config.cwd ?? sourceDir, {
+        ...(config.baseUrl ? { baseUrl: config.baseUrl } : {}),
+      }),
+      docsDir: stagedContentDir,
+    });
+    return {
+      cleanup: async () => {
+        await rm(stagedRoot, { force: true, recursive: true });
+      },
+      contentDir: stagedContentDir,
+      indexPages: result.indexPages,
+      nav: result.nav,
+      pages: result.pages,
+    };
+  } catch (error) {
+    await rm(stagedRoot, { force: true, recursive: true });
+    throw error;
+  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/leadtype/src/openapi/index.ts` around lines 1995 - 2020,
stageOpenApiDocs leaks the temporary staging directory if writeOpenApiPages
throws before cleanup is returned. Update stageOpenApiDocs to wrap the staging
and generation flow in a try/finally (or equivalent) so stagedRoot is removed on
any failure, and make sure the cleanup logic still covers the success path via
the returned cleanup function. Use the existing stageOpenApiDocs, mkdtemp,
writeOpenApiPages, and cleanup symbols to place the fix.

429-462: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Improved missing-spec error is good, but the fetch path still has no timeout.

The new resolved-path/cwd hint (Lines 447-454) is a solid improvement for local-file errors. However, the HTTP(S) branch (Lines 430-431) still calls fetch(input) with no timeout — an unresponsive remote spec URL can hang docs generation indefinitely. This was already flagged in a prior review with AbortSignal.timeout(...) as the suggested fix.

⏱️ Suggested fix
-    const response = await fetch(input);
+    const response = await fetch(input, { signal: AbortSignal.timeout(10_000) });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/leadtype/src/openapi/index.ts` around lines 429 - 462, The HTTP(S)
branch in readInput still uses fetch(input) without any timeout, so update the
remote-spec path to use an abortable request with a bounded timeout (for example
via AbortSignal.timeout or equivalent) and surface a clear timeout error before
continuing to parse the response; keep the existing fetch error handling and
apply this change only in the readInput function’s URL-loading path.
packages/leadtype/src/openapi/openapi.test.ts (1)

695-711: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Still missing a cleanup assertion for the stageOpenApiDocs failure path.

This test exercises stageOpenApiDocs throwing on a missing spec, but doesn't assert that the staged temp directory was removed. Given the temp-dir leak on failure flagged in index.ts (Lines 1995-2020) is still unresolved, this would be a good place to also assert cleanup, per the earlier recommendation to add failure-path coverage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/leadtype/src/openapi/openapi.test.ts` around lines 695 - 711, The
`stageOpenApiDocs` missing-spec failure test should also verify temp-directory
cleanup on error. Update the `stageOpenApiDocs` failure-path coverage in
`openapi.test.ts` to assert that the staged temp dir created by
`createTempDir()` is removed after the promise rejects, using the same setup
around `resolvedPath` and the missing `./missing.yaml` input. This will cover
the unresolved cleanup behavior in `stageOpenApiDocs` and ensure the failure
path does not leak staged files.
apps/tanstack/src/components/docs-mdx/api.tsx (1)

20-32: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

formatSchemaType duplicated with fumadocs formatApiSchemaType.

Same identical implementation exists in apps/fumadocs-example/lib/mdx-components.tsx (lines 158-168). Since flattenApiSchemaRows was already successfully shared via leadtype/mdx/openapi, extracting this formatter alongside it would remove the last bit of residual duplication.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/tanstack/src/components/docs-mdx/api.tsx` around lines 20 - 32, The
logic in formatSchemaType is duplicated with fumadocs’ formatApiSchemaType, so
extract this formatter into the shared leadtype/mdx/openapi module alongside
flattenApiSchemaRows and update api.tsx to import and use the shared helper.
Keep the behavior identical for schema.format/schema.type fallbacks, and remove
the local formatSchemaType implementation so both docs stacks share the same
source of truth.
apps/fumadocs-example/lib/mdx-components.tsx (1)

158-168: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

formatApiSchemaType still duplicated with TanStack's formatSchemaType.

This helper is byte-for-byte identical to formatSchemaType in apps/tanstack/src/components/docs-mdx/api.tsx (lines 24-31). The previous review's broader duplication concern was addressed by extracting flattenApiSchemaRows into the shared leadtype/mdx/openapi module — consider moving this small formatter there too so both apps stay in sync.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/fumadocs-example/lib/mdx-components.tsx` around lines 158 - 168, The
helper formatApiSchemaType is still duplicated with TanStack’s formatSchemaType,
so move this formatter into the shared leadtype/mdx/openapi module and have both
mdx components import it instead of keeping separate copies. Update the usages
in mdx-components and the TanStack docs API component to reference the shared
formatter so the behavior stays aligned in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@packages/leadtype/src/openapi/index.ts`:
- Around line 1550-1596: Extract the duplicated frontmatter assembly shared by
renderFrontmatter and renderIndexMdx into a small helper like
buildBaseFrontmatter so both paths use the same base fields. Have the helper
populate the common title/description, conditional group, type, source,
canonicalUrl, and lastModified fields, then let renderFrontmatter add
operation-specific metadata such as method, path, operationId, server,
apiVersion, tags, and deprecated. This will keep renderFrontmatter and
renderIndexMdx aligned and prevent future drift.

---

Duplicate comments:
In `@apps/fumadocs-example/lib/mdx-components.tsx`:
- Around line 158-168: The helper formatApiSchemaType is still duplicated with
TanStack’s formatSchemaType, so move this formatter into the shared
leadtype/mdx/openapi module and have both mdx components import it instead of
keeping separate copies. Update the usages in mdx-components and the TanStack
docs API component to reference the shared formatter so the behavior stays
aligned in one place.

In `@apps/tanstack/src/components/docs-mdx/api.tsx`:
- Around line 20-32: The logic in formatSchemaType is duplicated with fumadocs’
formatApiSchemaType, so extract this formatter into the shared
leadtype/mdx/openapi module alongside flattenApiSchemaRows and update api.tsx to
import and use the shared helper. Keep the behavior identical for
schema.format/schema.type fallbacks, and remove the local formatSchemaType
implementation so both docs stacks share the same source of truth.

In `@packages/leadtype/src/openapi/index.ts`:
- Around line 1995-2020: stageOpenApiDocs leaks the temporary staging directory
if writeOpenApiPages throws before cleanup is returned. Update stageOpenApiDocs
to wrap the staging and generation flow in a try/finally (or equivalent) so
stagedRoot is removed on any failure, and make sure the cleanup logic still
covers the success path via the returned cleanup function. Use the existing
stageOpenApiDocs, mkdtemp, writeOpenApiPages, and cleanup symbols to place the
fix.
- Around line 429-462: The HTTP(S) branch in readInput still uses fetch(input)
without any timeout, so update the remote-spec path to use an abortable request
with a bounded timeout (for example via AbortSignal.timeout or equivalent) and
surface a clear timeout error before continuing to parse the response; keep the
existing fetch error handling and apply this change only in the readInput
function’s URL-loading path.

In `@packages/leadtype/src/openapi/openapi.test.ts`:
- Around line 695-711: The `stageOpenApiDocs` missing-spec failure test should
also verify temp-directory cleanup on error. Update the `stageOpenApiDocs`
failure-path coverage in `openapi.test.ts` to assert that the staged temp dir
created by `createTempDir()` is removed after the promise rejects, using the
same setup around `resolvedPath` and the missing `./missing.yaml` input. This
will cover the unresolved cleanup behavior in `stageOpenApiDocs` and ensure the
failure path does not leak staged files.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c78b9f03-9ac9-4d9e-aa42-5668038f3226

📥 Commits

Reviewing files that changed from the base of the PR and between 10d54db and 358aace.

📒 Files selected for processing (18)
  • .changeset/openapi-generated-docs.md
  • apps/fumadocs-example/lib/mdx-components.tsx
  • apps/tanstack/src/components/docs-mdx/api.tsx
  • docs/changelog/0-4.mdx
  • docs/reference/openapi.mdx
  • packages/leadtype/package.json
  • packages/leadtype/rollup.config.ts
  • packages/leadtype/src/framework-adapters.test.ts
  • packages/leadtype/src/fumadocs/index.ts
  • packages/leadtype/src/internal/package-surface.test.ts
  • packages/leadtype/src/llm/llm.ts
  • packages/leadtype/src/markdown/plugins/openapi.ts
  • packages/leadtype/src/mdx/index.ts
  • packages/leadtype/src/mdx/openapi-schema-rows.ts
  • packages/leadtype/src/openapi/index.ts
  • packages/leadtype/src/openapi/openapi.test.ts
  • packages/leadtype/src/source/index.ts
  • packages/leadtype/src/source/source.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity
Prefer unknown over any when the type is genuinely unknown
Use const assertions (as const) for immutable values and literal types
Leverage TypeScript's type narrowing instead of type assertions

Files:

  • packages/leadtype/src/framework-adapters.test.ts
  • packages/leadtype/src/internal/package-surface.test.ts
  • packages/leadtype/src/fumadocs/index.ts
  • packages/leadtype/src/mdx/openapi-schema-rows.ts
  • packages/leadtype/src/source/source.test.ts
  • packages/leadtype/rollup.config.ts
  • packages/leadtype/src/llm/llm.ts
  • apps/tanstack/src/components/docs-mdx/api.tsx
  • packages/leadtype/src/mdx/index.ts
  • packages/leadtype/src/markdown/plugins/openapi.ts
  • apps/fumadocs-example/lib/mdx-components.tsx
  • packages/leadtype/src/openapi/openapi.test.ts
  • packages/leadtype/src/source/index.ts
  • packages/leadtype/src/openapi/index.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use meaningful variable names instead of magic numbers - extract constants with descriptive names
Use arrow functions for callbacks and short functions
Prefer for...of loops over .forEach() and indexed for loops
Use optional chaining (?.) and nullish coalescing (??) for safer property access
Prefer template literals over string concatenation
Use destructuring for object and array assignments
Use const by default, let only when reassignment is needed, never var
Always await promises in async functions - don't forget to use the return value
Use async/await syntax instead of promise chains for better readability
Handle errors appropriately in async code with try-catch blocks
Don't use async functions as Promise executors
Remove console.log, debugger, and alert statements from production code
Throw Error objects with descriptive messages, not strings or other values
Use try-catch blocks meaningfully - don't catch errors just to rethrow them
Prefer early returns over nested conditionals for error cases
Extract complex conditions into well-named boolean variables
Use early returns to reduce nesting
Prefer simple conditionals over nested ternary operators
Don't use eval() or assign directly to document.cookie
Avoid spread syntax in accumulators within loops
Use top-level regex literals instead of creating them in loops
Prefer specific imports over namespace imports
Use descriptive names for functions, variables, and types for meaningful naming
Add comments for complex logic, but prefer self-documenting code

Files:

  • packages/leadtype/src/framework-adapters.test.ts
  • packages/leadtype/src/internal/package-surface.test.ts
  • packages/leadtype/src/fumadocs/index.ts
  • packages/leadtype/src/mdx/openapi-schema-rows.ts
  • packages/leadtype/src/source/source.test.ts
  • packages/leadtype/rollup.config.ts
  • packages/leadtype/src/llm/llm.ts
  • apps/tanstack/src/components/docs-mdx/api.tsx
  • packages/leadtype/src/mdx/index.ts
  • packages/leadtype/src/markdown/plugins/openapi.ts
  • apps/fumadocs-example/lib/mdx-components.tsx
  • packages/leadtype/src/openapi/openapi.test.ts
  • packages/leadtype/src/source/index.ts
  • packages/leadtype/src/openapi/index.ts
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{test,spec}.{js,ts,jsx,tsx}: Write assertions inside it() or test() blocks
Avoid done callbacks in async tests - use async/await instead
Don't use .only or .skip in committed code
Keep test suites reasonably flat - avoid excessive describe nesting

Files:

  • packages/leadtype/src/framework-adapters.test.ts
  • packages/leadtype/src/internal/package-surface.test.ts
  • packages/leadtype/src/source/source.test.ts
  • packages/leadtype/src/openapi/openapi.test.ts
**/index.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Avoid barrel files (index files that re-export everything)

Files:

  • packages/leadtype/src/fumadocs/index.ts
  • packages/leadtype/src/mdx/index.ts
  • packages/leadtype/src/source/index.ts
  • packages/leadtype/src/openapi/index.ts
**/*.{jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{jsx,tsx}: Use function components over class components in React
Call hooks at the top level only, never conditionally
Specify all dependencies in hook dependency arrays correctly
Use the key prop for elements in iterables (prefer unique IDs over array indices)
Nest children between opening and closing tags instead of passing as props
Don't define components inside other components
Avoid dangerouslySetInnerHTML unless absolutely necessary
Use proper image components (e.g., Next.js <Image>) over <img> tags
Use Next.js <Image> component for images
Use next/head or App Router metadata API for head elements in Next.js
Use Server Components for async data fetching instead of async Client Components in Next.js
Use ref as a prop instead of React.forwardRef in React 19+

Files:

  • apps/tanstack/src/components/docs-mdx/api.tsx
  • apps/fumadocs-example/lib/mdx-components.tsx
**/*.{jsx,tsx,html}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{jsx,tsx,html}: Use semantic HTML and ARIA attributes for accessibility: provide meaningful alt text for images, use proper heading hierarchy, add labels for form inputs, include keyboard event handlers alongside mouse events, use semantic elements instead of divs with roles
Add rel="noopener" when using target="_blank" on links

Files:

  • apps/tanstack/src/components/docs-mdx/api.tsx
  • apps/fumadocs-example/lib/mdx-components.tsx
🧠 Learnings (1)
📚 Learning: 2026-06-09T18:30:08.038Z
Learnt from: KayleeWilliams
Repo: inthhq/leadtype PR: 97
File: .changeset/search-prototype-safety-and-scaling.md:5-5
Timestamp: 2026-06-09T18:30:08.038Z
Learning: In this repo, `.changeset/*.md` files must not start the body with an H1/first-line heading (`#`) immediately after the YAML frontmatter. The changesets tool inlines the body as bullet entries into `CHANGELOG.md` during release, and a leading `#` heading would break the generated changelog format. As a result, MD041 (`first-line-heading`) warnings for files under `.changeset/` are expected false positives and should be ignored.

Applied to files:

  • .changeset/openapi-generated-docs.md
🪛 ast-grep (0.44.1)
packages/leadtype/src/openapi/openapi.test.ts

[warning] 706-708: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(
${resolvedPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}.*openapiCwd
)
Note: [CWE-1333] Inefficient Regular Expression Complexity

(regexp-from-variable)

packages/leadtype/src/openapi/index.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🔇 Additional comments (27)
packages/leadtype/src/openapi/index.ts (3)

1-251: LGTM!


1084-1099: LGTM! Path-parameter substitution correctly skips unresolved placeholders and matches the new test coverage.


1238-1251: LGTM! Using a Map keyed by in:name correctly gives operation-level parameters precedence over path-item parameters while preserving original insertion order, matching OpenAPI's override semantics.

packages/leadtype/src/openapi/openapi.test.ts (1)

114-131: LGTM! New assertions correctly reflect the unquoted YAML frontmatter format and the path-parameter substitution/precedence behavior in index.ts.

Also applies to: 146-174, 522-631

packages/leadtype/src/mdx/openapi-schema-rows.ts (1)

1-75: LGTM! Clean extraction of the shared flattening logic with a sensible depth guard against runaway/cyclic schemas.

packages/leadtype/src/markdown/plugins/openapi.ts (1)

80-113: LGTM! Delegating to the shared flattenApiSchemaRows removes the duplicated local flattening logic while preserving the same table shape.

docs/changelog/0-4.mdx (1)

12-19: LGTM!

docs/reference/openapi.mdx (2)

135-139: 🎯 Functional Correctness

Verify the helper is actually client-safe.

The docs promise leadtype/mdx/openapi has no dependencies and is safe to import from client components. Please verify the published subpath is fully browser-compatible before documenting that guarantee; otherwise this guidance can cause client builds to break.


205-261: LGTM!

.changeset/openapi-generated-docs.md (1)

5-9: LGTM!

apps/fumadocs-example/lib/mdx-components.tsx (3)

170-248: 🩺 Stability & Availability | ⚡ Quick win

Verify the previously-flagged duplicate-key issue in ApiAuth requirements is actually fixed.

A prior review flagged that key={names.join("+") || "anonymous"} for the requirements list can collide when multiple OR'd security requirements resolve to the same scope-name set. This range isn't included in the current diff snippet, and nothing in the change summary indicates the key generation was updated to include the array index. Please confirm the fix (index + label) is present.

#!/bin/bash
fd mdx-components.tsx apps/fumadocs-example --exec rg -n -B2 -A6 'requirements.map' {}

8-27: LGTM!


125-156: LGTM!

Good move switching SchemaRows to the shared flattenApiSchemaRows helper — this resolves the bulk of the previously-flagged duplication with the TanStack implementation.

Also applies to: 323-323

apps/tanstack/src/components/docs-mdx/api.tsx (2)

162-188: 🩺 Stability & Availability | ⚡ Quick win

Verify the previously-flagged duplicate-key issue in ApiAuth requirements is actually fixed.

A prior review flagged key={label} colliding for repeated/identical anonymous or scope-based requirement labels, recommending the loop index be included. This range isn't shown in the current diff, and the change summary doesn't indicate the key generation changed. Please confirm the fix is present.

#!/bin/bash
fd api.tsx apps/tanstack/src/components/docs-mdx --exec rg -n -B2 -A6 'requirements.map' {}

33-72: LGTM!

Nice consolidation onto the shared flattenApiSchemaRows helper for SchemaTable/MediaType.

Also applies to: 102-118

packages/leadtype/src/mdx/index.ts (1)

45-50: LGTM!

Also applies to: 61-69

packages/leadtype/package.json (2)

38-42: LGTM!


75-78: 🗄️ Data Integrity & Integration

Add a default condition to ./openapi if this subpath should resolve outside import-only consumers.
The export currently exposes only types and import; adding "default": "./dist/openapi/index.js" would match the sibling ./mdx/openapi shape and avoid resolution failures for broader condition sets.

packages/leadtype/src/internal/package-surface.test.ts (1)

86-86: LGTM!

Also applies to: 95-95

packages/leadtype/src/llm/llm.ts (2)

33-33: LGTM!

Also applies to: 477-481, 841-845


3404-3434: 🎯 Functional Correctness

Verify mounts shouldn't be scoped away from the OpenAPI overlay dir.

Both the primary readSourceDocs call and the extraDocsDirs overlay loop reuse the same config.mounts. mounts presumably encodes remap rules for the authored docs tree; applying it unmodified to a generated OpenAPI overlay directory (a completely different structure, e.g. api/index.mdx) could cause unintended remapping/exclusion of generated pages if any mount pattern happens to match the overlay's relative paths.

Please confirm readSourceDocs's mount-matching semantics tolerate being applied to an unrelated overlay tree, or scope mounts out of the overlay call.

packages/leadtype/rollup.config.ts (1)

10-10: LGTM!

Also applies to: 19-19

packages/leadtype/src/framework-adapters.test.ts (1)

65-65: LGTM!

packages/leadtype/src/fumadocs/index.ts (1)

74-75: LGTM!

Also applies to: 117-117

packages/leadtype/src/source/index.ts (2)

18-20: LGTM!

Also applies to: 51-55, 71-92, 174-184, 211-213, 420-468


481-542: 🩺 Stability & Availability

Remove the overlay from contentRoots and invalidate the file caches on cleanup

cleanup() should also drop the overlay path from contentRoots and clear cachedFilesByRoot/cachedFiles; otherwise a later listPages()/buildSearchIndex() call can still crawl the deleted temp dir and reuse stale paths.

packages/leadtype/src/source/source.test.ts (1)

1-1: LGTM!

Also applies to: 14-40, 419-506

- Encode JSX string attributes so spec quotes cannot inject MDX (mdxProp)
- Collision-suffix overview pages when specs share an output dir and
  thread the real overview slug through nav, canonicalUrl, and Related
- Accept OpenAPI-only docs configs in leadtype generate validation
- Time out remote spec and $ref fetches after 30s
- Clean up staging temp dir when stageOpenApiDocs generation fails
- Skip OpenAPI generation under --include/--exclude with a warning
- Fail loudly before overwriting pre-existing files in writeOpenApiPages
- Wrap tanstack llm-generate staging in try/finally
- De-duplicate auth requirement React keys in example renderers
- Document the spec trust model and filter behavior
@KayleeWilliams KayleeWilliams merged commit 4738c1c into main Jul 6, 2026
4 checks passed
@KayleeWilliams KayleeWilliams deleted the feat/openapi-docs-generation branch July 7, 2026 09:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant