Add framework adapters and dogfood examples#54
Conversation
📝 WalkthroughSummary by CodeRabbit
WalkthroughAdds core framework utilities and adapter entrypoints for Astro, Next, Nuxt, SvelteKit, and TanStack; implements a framework-neutral search client plus React/Vue/Svelte wrappers; provides fully working example apps for each framework; expands package exports and Rollup entries; and adds tests, docs, linter, and ignore updates. ChangesMulti-framework Integration Adapters and Search
Estimated code review effort Possibly related PRs
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a45c2789a4
ℹ️ 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".
| const response = await fetch( | ||
| new URL( | ||
| joinUrlPath(config.publicPathPrefix ?? "/", target.filePath), | ||
| url | ||
| ) |
There was a problem hiding this comment.
Avoid recursively fetching markdown through Proxy
When this helper is used with the documented matcher in apps/next-example/proxy.ts (/docs/:path*), the internal fetch(new URL('/docs/…md', url)) is intercepted by the same Proxy before Next serves the file from public/. A request for /docs/quickstart.md (or an HTML request negotiated to markdown) therefore re-enters createDocsProxy, which fetches /docs/quickstart.md again and can recurse until the request times out instead of returning the generated markdown. The proxy needs to fetch from a path excluded by the matcher or the example/config needs to exclude generated .md assets from Proxy.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
TL;DR — Adds first-party adapters for Astro, Nuxt/Nitro, SvelteKit, and TanStack Start; consolidates duplicated route-handler plumbing into internal/framework.ts; splits the search client into framework-neutral + per-framework hooks; and adds four dogfood apps over a shared MDX fixture. Two minor nits below; nothing blocking.
Key changes
- Centralize adapter plumbing in
internal/framework.ts— extractscreateAgentArtifactHandler,createLoadPage,listJoinedSlugs,createPublicMarkdownReader, and slug helpers so every adapter shares one implementation. - Add
leadtype/{astro,nuxt,sveltekit,tanstack-start}— each adapter exposes a static-paths factory, acreateLoadPageData, and a server/markdown handler over the shared internal helpers. - Split search clients —
search/client.tsholds the framework-neutralcreateSearchClient(module-level artifact cache, stale-result protection);search/{react,vue,svelte}.tswrap it;next/client.tsbecomes a re-export shell. - Add
createDocsProxyfor Next Edge — fetches generated markdown from Next's static asset serving since Proxy can't read the filesystem. - Dogfood apps —
apps/{astro,next,sveltekit,nuxt}-example/all render the sameexamples/shared-docs/fixture. - Expand
package-surface.test.ts— adds new adapter directories and a separateFRAMEWORK_RUNTIME_DIRECTORIESlist so the framework-runtime ban also exemptssearch/{react,vue,svelte}.ts.
Summary | 75 files | 3 commits | base: main ← framework-adapters-dogfood
Shared internal framework module
Before: Each adapter duplicated artifact routing, markdown reading, and slug helpers.
After:internal/framework.tsowns the shared primitives; adapters thin to per-framework glue.
createAgentArtifactHandler dual-mounts the three artifact files at both the unscoped root and the scoped <artifactBasePath>/... paths. Which set is reachable depends on framework wiring (a /docs/[...slug] catch-all won't see /robots.txt, but a root handler will); the fall-through to createAgentMarkdownResponse is unchanged. createPublicMarkdownReader keeps the previous Next traversal guard (relative.startsWith("..") || path.isAbsolute(relative)) verbatim.
packages/leadtype/src/internal/framework.ts · packages/leadtype/src/next/index.ts
Framework-neutral search client
Before: Search hook + BM25 loader lived together in
next/client.tsas React-only.
After:search/client.tsexposes a framework-freecreateSearchClientandresolveSearchArtifactUrls; the React hook moves tosearch/react.ts; Vue and Svelte get their own wrappers.
Each per-framework wrapper preserves stale-result protection — the React hook keeps a latestQueryRef, while Vue and Svelte use a monotonically incrementing requestId so older awaited searches drop their results when a newer one starts.
packages/leadtype/src/search/client.ts · packages/leadtype/src/search/react.ts · packages/leadtype/src/search/svelte.ts · packages/leadtype/src/search/vue.ts
Dogfood apps over a single MDX fixture
Before: Only
apps/exampleandapps/fumadocs-exampleexercised the package.
After: Four apps (Astro, Next, SvelteKit, Nuxt) render the sameexamples/shared-docs/fixture through their respective adapters and ship a working browser search box.
Each app runs leadtype generate --src ../../examples/shared-docs --out public as part of dev/build/check-types, so adapter wiring is exercised against generated artifacts on every type-check. Note that every example repeats the same { ...manifestJson, version: 1 } as unknown as AgentReadabilityManifest cast — internal/framework.ts exports a normalizeManifest that does exactly this but has no callers; consider wiring it in or trimming the duplication.
examples/shared-docs/docs/docs.config.ts · apps/astro-example/src/pages/docs/[...slug].astro · apps/sveltekit-example/src/routes/docs/[...slug]/+page.svelte
Claude Opus | 𝕏
| manifest: AgentReadabilityManifest | ||
| ): AgentReadabilityManifest { | ||
| return { ...manifest, version: 1 }; | ||
| } |
There was a problem hiding this comment.
normalizeManifest is exported but never called anywhere in the repo (grep confirms). Every dogfood app open-codes the same { ...manifestJson, version: 1 } as unknown as AgentReadabilityManifest cast — either wire this helper in (or expose it through each adapter’s public surface) or drop the export to keep the internal module honest.
| name: string | ||
| ): string | undefined { | ||
| const value = headers?.[name] ?? headers?.[name.toLowerCase()]; | ||
| return Array.isArray(value) ? value[0] : value; |
There was a problem hiding this comment.
Both call sites pass already-lowercase strings ("host", "x-forwarded-proto"), and Node’s http layer normalizes incoming header keys to lowercase before populating req.headers. The name.toLowerCase() fallback never fires — consider dropping it to keep the helper to a single lookup.
There was a problem hiding this comment.
Actionable comments posted: 15
🤖 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/astro-example/src/pages/docs/`[...slug].astro:
- Around line 45-51: The input event handler that calls await
client.search(query) should be wrapped in try/catch/finally: inside the try call
const matches = await client.search(query) and render results; in catch handle
and log the error (and show a user-facing message in status or results) so
unhandled rejections don't occur; in finally ensure status.textContent is reset
from "loading" (e.g., to "ready" or "idle") and any necessary cleanup
(results.replaceChildren() remains correct) so the UI never gets stuck in the
"loading" state; update the anonymous callback passed to input.addEventListener
accordingly to reference client.search, status.textContent, and
results.replaceChildren.
In `@apps/astro-example/src/pages/docs/`[...slug].md.ts:
- Around line 6-9: The object construction using `manifest` currently uses an
unsafe double assertion (`as unknown as AgentReadabilityManifest`) which
bypasses structural checks; replace that with TypeScript's `satisfies` operator
to validate the shape at compile time: keep the spread of `manifestJson` and
`version: 1`, and change the trailing cast to `satisfies
AgentReadabilityManifest` so the compiler enforces the structure of `manifest`
without turning off type safety for `manifestJson`/`manifest`.
In `@apps/next-example/components/search-box.tsx`:
- Line 15: The span that renders the dynamic status value (status) in SearchBox
should be announced by assistive tech, so change the element that currently
renders <span>{status}</span> to be a live region by adding ARIA attributes
(e.g., aria-live="polite" and aria-atomic="true", or role="status") so screen
readers announce updates; keep the variable name status and the SearchBox
component unchanged, and optionally apply a visually-hidden/sr-only CSS class if
you don't want the status visible but still announced.
In `@apps/next-example/next.config.mjs`:
- Around line 9-12: Replace the CWD-based resolution used for typeTableBasePath
with a path based on the config file location: instead of
path.resolve(process.cwd(), "../../examples/shared-docs") compute the directory
of this ESM module via fileURLToPath(import.meta.url) and path.dirname(...) and
then resolve "../../examples/shared-docs" against that; update the
typeTableBasePath assignment to use
path.resolve(path.dirname(fileURLToPath(import.meta.url)),
"../../examples/shared-docs") (ensure fileURLToPath is imported from "url") so
the docs fixture path is stable regardless of invocation CWD.
In `@apps/next-example/proxy.ts`:
- Around line 5-8: The manifest object is being force-cast with "as unknown as
AgentReadabilityManifest", bypassing TypeScript checks; replace this double
assertion by using a const assertion for the version literal and the `satisfies`
operator so the spreaded manifestJson keeps inferred types but is validated
against AgentReadabilityManifest at compile time—update the `manifest`
declaration (the variable named manifest built from manifestJson and version) to
use `version: 1 as const` (or include the literal in an `as const` object) and
append `satisfies AgentReadabilityManifest` instead of the double cast.
In `@apps/nuxt-example/server/routes/docs/`[...slug].md.ts:
- Around line 6-9: The manifest variable is using a double type assertion ("as
unknown as AgentReadabilityManifest") which bypasses compile-time shape checks;
replace that assertion by using TypeScript's satisfies operator so the object
created from manifestJson is validated against AgentReadabilityManifest (i.e.,
change the declaration of manifest that spreads manifestJson and sets version to
use "satisfies AgentReadabilityManifest" instead of "as unknown as
AgentReadabilityManifest"), keeping the same object shape and runtime value but
restoring proper type-level validation for manifest/manifestJson.
In `@apps/sveltekit-example/src/app.html`:
- Around line 3-7: Add a default <title> element into the document head so pages
without an explicit title aren’t untitled; update the head block that contains
%sveltekit.head% by adding a descriptive default title (e.g., a site name or
"Untitled Page") before the %sveltekit.head% token so the <title> is present
even when individual pages don’t set one.
In `@apps/sveltekit-example/src/lib/source.ts`:
- Around line 1-6: Replace the process.cwd()-based fixtureRoot computation with
a file-location-based resolution using import.meta.url: update the code that
defines fixtureRoot (currently using path.resolve(process.cwd(),
"../../examples/shared-docs")) to instead resolve the examples/shared-docs
directory relative to the current module file via path.dirname(new
URL(import.meta.url).pathname) or equivalent; change the fixtureRoot initializer
in apps/sveltekit-example/src/lib/source.ts (variable name: fixtureRoot) and
apply the identical fix in svelte.config.js so the path is correct regardless of
the working directory.
In `@apps/sveltekit-example/src/routes/docs/`[...slug]/+page.server.ts:
- Around line 1-2: The export `load` in +page.server.ts should be explicitly
typed as SvelteKit's PageServerLoad; add an import like `import type {
PageServerLoad } from './$types'` and change the signature to `export const
load: PageServerLoad = async (event) => { ... }` (or destructure the same
params) so the route's server load contract is enforced and types for
params/locals are available; keep the existing implementation but ensure the
parameter shape matches the PageServerLoad type.
In `@docs/build/framework-matrix.mdx`:
- Around line 12-19: The docs table lists non-existent dogfood apps for
"TanStack Start" and "Fumadocs" causing inconsistency; update
docs/build/framework-matrix.mdx by either removing the "TanStack Start" and
"Fumadocs" rows from the table or changing their "Dogfood app" column entries to
"Planned" or "Coming soon" (choose one approach consistently), and ensure the
verification section that enumerates implemented dogfood apps is updated to
match the table (so the set of examples in the verification text matches the
four actual apps: Next.js, Astro, SvelteKit, Nuxt).
In `@packages/leadtype/src/astro/index.ts`:
- Around line 65-80: Current code unconditionally treats any request with
context.params.slug as an artifact markdown request and drops original headers,
causing valid artifact paths (e.g. /docs/sitemap.md) to be routed incorrectly
and lose headers; fix by first checking that the request pathname corresponds to
the artifactBasePath (config.artifactBasePath or "/docs") before calling
createAgentMarkdownResponse for `${slug}.md`, and pass the original request
headers (e.g., Array.from(context.request.headers) or cloning
context.request.headers) instead of an empty object; locate the logic around
context.params.slug, createAgentMarkdownResponse, joinUrlPath and replace the
unconditional short-circuit with a conditional that verifies the request path is
under artifactBasePath and forwards request headers to
createAgentMarkdownResponse, otherwise fall through to handler(context.request).
In `@packages/leadtype/src/next/index.ts`:
- Around line 183-191: readMarkdownFile may throw if fetch fails, causing a 500
instead of falling back; wrap the fetch and response handling for the
constructed URL (using joinUrlPath and url within readMarkdownFile) in a
try-catch, and on any thrown error return null (optionally log the error) so
callers treat missing artifacts gracefully; ensure the function signature
(readMarkdownFile(target: MarkdownMirrorTarget)) still returns
Promise<string|null>.
In `@packages/leadtype/src/nuxt/index.ts`:
- Around line 65-68: Sanitize the x-forwarded-proto before building the URL:
read the header via headerValue(headers, "x-forwarded-proto"), split on commas
and take the first segment, trim and lowercase it, then validate it is "http" or
"https" (fallback to "http" if not) and assign that to the proto variable used
in the new URL(...) call; update the proto assignment near the URL construction
(referencing proto, headerValue, new URL, event.path and req?.url) so
comma-separated values or unexpected values cannot cause new URL to throw.
In `@packages/leadtype/src/search/react.ts`:
- Around line 72-73: The current stale-response check compares
latestQueryRef.current to the query string (next), which fails when overlapping
requests use the same string; instead implement request sequencing: add a
numeric/latestRequestIdRef (useRef<number>(0)), increment it when dispatching a
new request, capture the current id in the local request (e.g., localRequestId =
++latestRequestIdRef.current) and when the async response resolves only apply
state if localRequestId === latestRequestIdRef.current; update places currently
using latestQueryRef/timeoutRef for suppression to use this id-based check (keep
latestQueryRef for storing the string if needed, and clear timeoutRef logic
unchanged).
🪄 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: 5479db62-7cbc-4de0-9dcd-8b75a5ae07f6
⛔ Files ignored due to path filters (6)
apps/example/src/generated/agent-readability.jsonis excluded by!**/generated/**apps/example/src/generated/docs-nav.jsonis excluded by!**/generated/**apps/example/src/generated/docs-pages.jsonis excluded by!**/generated/**apps/example/src/generated/docs-search-content.jsonis excluded by!**/generated/**apps/example/src/generated/docs-search-index.jsonis excluded by!**/generated/**bun.lockis excluded by!**/*.lock
📒 Files selected for processing (69)
.gitignoreapps/astro-example/astro.config.mjsapps/astro-example/package.jsonapps/astro-example/public/styles.cssapps/astro-example/src/lib/source.tsapps/astro-example/src/pages/docs/[...slug].astroapps/astro-example/src/pages/docs/[...slug].md.tsapps/astro-example/src/pages/index.astroapps/astro-example/tsconfig.jsonapps/next-example/app/docs/[[...slug]]/page.tsxapps/next-example/app/layout.tsxapps/next-example/app/page.tsxapps/next-example/app/styles.cssapps/next-example/components/search-box.tsxapps/next-example/lib/mdx-components.tsxapps/next-example/lib/source.tsapps/next-example/next-env.d.tsapps/next-example/next.config.mjsapps/next-example/package.jsonapps/next-example/proxy.tsapps/next-example/tsconfig.jsonapps/nuxt-example/app.vueapps/nuxt-example/assets/styles.cssapps/nuxt-example/lib/source.tsapps/nuxt-example/nuxt.config.tsapps/nuxt-example/package.jsonapps/nuxt-example/pages/docs/[[slug]].vueapps/nuxt-example/pages/index.vueapps/nuxt-example/server/api/docs.get.tsapps/nuxt-example/server/middleware/agent-readability.tsapps/nuxt-example/server/routes/docs/[...slug].md.tsapps/nuxt-example/tsconfig.jsonapps/sveltekit-example/package.jsonapps/sveltekit-example/src/app.cssapps/sveltekit-example/src/app.htmlapps/sveltekit-example/src/lib/source.tsapps/sveltekit-example/src/routes/+layout.svelteapps/sveltekit-example/src/routes/+page.svelteapps/sveltekit-example/src/routes/docs/[...slug].md/+server.tsapps/sveltekit-example/src/routes/docs/[...slug]/+page.server.tsapps/sveltekit-example/src/routes/docs/[...slug]/+page.svelteapps/sveltekit-example/svelte.config.jsapps/sveltekit-example/tsconfig.jsonapps/sveltekit-example/vite.config.tsbiome.jsoncdocs/build/framework-matrix.mdxdocs/docs.config.tsdocs/quickstart.mdxexamples/shared-docs/README.mdexamples/shared-docs/docs/components.mdxexamples/shared-docs/docs/docs.config.tsexamples/shared-docs/docs/index.mdxexamples/shared-docs/docs/quickstart.mdxexamples/shared-docs/docs/search.mdxpackages/leadtype/package.jsonpackages/leadtype/rollup.config.tspackages/leadtype/src/astro/index.tspackages/leadtype/src/framework-adapters.test.tspackages/leadtype/src/internal/framework.tspackages/leadtype/src/internal/package-surface.test.tspackages/leadtype/src/next/client.tspackages/leadtype/src/next/index.tspackages/leadtype/src/nuxt/index.tspackages/leadtype/src/search/client.tspackages/leadtype/src/search/react.tspackages/leadtype/src/search/svelte.tspackages/leadtype/src/search/vue.tspackages/leadtype/src/sveltekit/index.tspackages/leadtype/src/tanstack-start/index.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{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
Addrel="noopener"when usingtarget="_blank"on links
Files:
apps/sveltekit-example/src/app.htmlapps/next-example/lib/mdx-components.tsxapps/next-example/app/layout.tsxapps/next-example/app/docs/[[...slug]]/page.tsxapps/next-example/app/page.tsxapps/next-example/components/search-box.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity
Preferunknownoveranywhen 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:
apps/next-example/lib/mdx-components.tsxapps/next-example/app/layout.tsxapps/nuxt-example/server/middleware/agent-readability.tsapps/nuxt-example/lib/source.tsapps/sveltekit-example/vite.config.tsapps/sveltekit-example/src/lib/source.tsapps/next-example/next-env.d.tsapps/nuxt-example/server/api/docs.get.tsapps/nuxt-example/nuxt.config.tsapps/next-example/app/docs/[[...slug]]/page.tsxapps/sveltekit-example/src/routes/docs/[...slug].md/+server.tspackages/leadtype/src/tanstack-start/index.tsdocs/docs.config.tsapps/next-example/lib/source.tsapps/next-example/app/page.tsxapps/astro-example/src/pages/docs/[...slug].md.tsexamples/shared-docs/docs/docs.config.tsapps/sveltekit-example/src/routes/docs/[...slug]/+page.server.tsapps/nuxt-example/server/routes/docs/[...slug].md.tsapps/astro-example/src/lib/source.tsapps/next-example/components/search-box.tsxpackages/leadtype/src/search/svelte.tspackages/leadtype/src/astro/index.tspackages/leadtype/src/sveltekit/index.tsapps/next-example/proxy.tspackages/leadtype/rollup.config.tspackages/leadtype/src/search/client.tspackages/leadtype/src/search/vue.tspackages/leadtype/src/next/index.tspackages/leadtype/src/next/client.tspackages/leadtype/src/nuxt/index.tspackages/leadtype/src/search/react.tspackages/leadtype/src/internal/framework.tspackages/leadtype/src/internal/package-surface.test.tspackages/leadtype/src/framework-adapters.test.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
Preferfor...ofloops over.forEach()and indexedforloops
Use optional chaining (?.) and nullish coalescing (??) for safer property access
Prefer template literals over string concatenation
Use destructuring for object and array assignments
Useconstby default,letonly when reassignment is needed, nevervar
Alwaysawaitpromises in async functions - don't forget to use the return value
Useasync/awaitsyntax 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
Removeconsole.log,debugger, andalertstatements from production code
ThrowErrorobjects with descriptive messages, not strings or other values
Usetry-catchblocks 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 useeval()or assign directly todocument.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:
apps/next-example/lib/mdx-components.tsxapps/next-example/app/layout.tsxapps/nuxt-example/server/middleware/agent-readability.tsapps/nuxt-example/lib/source.tsapps/sveltekit-example/vite.config.tsapps/sveltekit-example/src/lib/source.tsapps/next-example/next-env.d.tsapps/nuxt-example/server/api/docs.get.tsapps/nuxt-example/nuxt.config.tsapps/sveltekit-example/svelte.config.jsapps/next-example/app/docs/[[...slug]]/page.tsxapps/sveltekit-example/src/routes/docs/[...slug].md/+server.tspackages/leadtype/src/tanstack-start/index.tsdocs/docs.config.tsapps/next-example/lib/source.tsapps/next-example/app/page.tsxapps/astro-example/src/pages/docs/[...slug].md.tsexamples/shared-docs/docs/docs.config.tsapps/sveltekit-example/src/routes/docs/[...slug]/+page.server.tsapps/nuxt-example/server/routes/docs/[...slug].md.tsapps/astro-example/src/lib/source.tsapps/next-example/components/search-box.tsxpackages/leadtype/src/search/svelte.tspackages/leadtype/src/astro/index.tspackages/leadtype/src/sveltekit/index.tsapps/next-example/proxy.tspackages/leadtype/rollup.config.tspackages/leadtype/src/search/client.tspackages/leadtype/src/search/vue.tspackages/leadtype/src/next/index.tspackages/leadtype/src/next/client.tspackages/leadtype/src/nuxt/index.tspackages/leadtype/src/search/react.tspackages/leadtype/src/internal/framework.tspackages/leadtype/src/internal/package-surface.test.tspackages/leadtype/src/framework-adapters.test.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 thekeyprop 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
AvoiddangerouslySetInnerHTMLunless absolutely necessary
Use proper image components (e.g., Next.js<Image>) over<img>tags
Use Next.js<Image>component for images
Usenext/heador 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 ofReact.forwardRefin React 19+
Files:
apps/next-example/lib/mdx-components.tsxapps/next-example/app/layout.tsxapps/next-example/app/docs/[[...slug]]/page.tsxapps/next-example/app/page.tsxapps/next-example/components/search-box.tsx
**/*.{svelte,vue}
📄 CodeRabbit inference engine (AGENTS.md)
Use
classandforattributes (notclassNameorhtmlFor) in Solid/Svelte/Vue/Qwik
Files:
apps/sveltekit-example/src/routes/+layout.svelteapps/sveltekit-example/src/routes/+page.svelteapps/nuxt-example/app.vueapps/sveltekit-example/src/routes/docs/[...slug]/+page.svelteapps/nuxt-example/pages/index.vueapps/nuxt-example/pages/docs/[[slug]].vue
**/index.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Avoid barrel files (index files that re-export everything)
Files:
packages/leadtype/src/tanstack-start/index.tspackages/leadtype/src/astro/index.tspackages/leadtype/src/sveltekit/index.tspackages/leadtype/src/next/index.tspackages/leadtype/src/nuxt/index.ts
**/*.{test,spec}.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{test,spec}.{js,ts,jsx,tsx}: Write assertions insideit()ortest()blocks
Avoid done callbacks in async tests - use async/await instead
Don't use.onlyor.skipin committed code
Keep test suites reasonably flat - avoid excessivedescribenesting
Files:
packages/leadtype/src/internal/package-surface.test.tspackages/leadtype/src/framework-adapters.test.ts
🪛 HTMLHint (1.9.2)
apps/sveltekit-example/src/app.html
[error] 7-7: <title> must be present in tag.
(title-require)
🪛 LanguageTool
docs/build/framework-matrix.mdx
[style] ~52-~52: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ch/reactin client search components. UsecreateDocsProxy()` when a Next Proxy o...
(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
🔇 Additional comments (60)
packages/leadtype/package.json (1)
36-39: LGTM!Also applies to: 44-47, 76-91, 116-123, 170-170, 179-179, 191-193, 220-222, 225-228
packages/leadtype/rollup.config.ts (1)
10-10: LGTM!Also applies to: 12-12, 20-23, 30-31
packages/leadtype/src/internal/package-surface.test.ts (1)
12-25: LGTM!Also applies to: 82-82, 84-84, 92-95, 102-103, 168-174, 182-182
examples/shared-docs/README.md (1)
1-3: LGTM!examples/shared-docs/docs/components.mdx (1)
1-19: LGTM!examples/shared-docs/docs/docs.config.ts (1)
1-33: LGTM!examples/shared-docs/docs/index.mdx (1)
1-28: LGTM!examples/shared-docs/docs/quickstart.mdx (1)
1-24: LGTM!examples/shared-docs/docs/search.mdx (1)
1-21: LGTM!docs/build/framework-matrix.mdx (1)
21-127: LGTM!docs/docs.config.ts (1)
20-20: LGTM!docs/quickstart.mdx (1)
105-105: LGTM!.gitignore (1)
27-43: LGTM!biome.jsonc (2)
20-20: LGTM!
56-83: LGTM!packages/leadtype/src/search/client.ts (1)
1-160: LGTM!packages/leadtype/src/search/svelte.ts (1)
1-115: LGTM!packages/leadtype/src/search/vue.ts (1)
1-75: LGTM!packages/leadtype/src/next/client.ts (1)
3-14: LGTM!packages/leadtype/src/internal/framework.ts (1)
46-58: LGTM!Also applies to: 60-85, 87-114, 116-211
packages/leadtype/src/sveltekit/index.ts (1)
26-53: LGTM!packages/leadtype/src/tanstack-start/index.ts (1)
22-49: LGTM!packages/leadtype/src/framework-adapters.test.ts (1)
96-201: LGTM!apps/astro-example/astro.config.mjs (1)
1-19: LGTM!apps/astro-example/public/styles.css (1)
1-68: LGTM!apps/astro-example/src/lib/source.ts (1)
1-12: LGTM!apps/astro-example/src/pages/docs/[...slug].md.ts (1)
11-17: LGTM!apps/astro-example/src/pages/index.astro (1)
1-17: LGTM!apps/astro-example/tsconfig.json (1)
1-7: LGTM!apps/next-example/package.json (1)
1-30: LGTM!apps/next-example/proxy.ts (1)
10-22: LGTM!apps/next-example/tsconfig.json (1)
1-16: LGTM!apps/next-example/app/docs/[[...slug]]/page.tsx (1)
1-40: LGTM!apps/next-example/app/layout.tsx (1)
1-11: LGTM!apps/next-example/app/page.tsx (1)
1-18: LGTM!apps/next-example/app/styles.css (1)
1-78: LGTM!apps/next-example/components/search-box.tsx (1)
1-14: LGTM!Also applies to: 16-27
apps/next-example/lib/mdx-components.tsx (1)
1-34: LGTM!apps/next-example/lib/source.ts (1)
1-13: LGTM!apps/next-example/next-env.d.ts (1)
1-7: LGTM!apps/nuxt-example/app.vue (1)
1-5: LGTM!apps/nuxt-example/assets/styles.css (1)
1-63: LGTM!apps/nuxt-example/lib/source.ts (1)
1-18: LGTM!apps/nuxt-example/nuxt.config.ts (1)
1-13: LGTM!Also applies to: 15-17
apps/nuxt-example/package.json (1)
1-26: LGTM!apps/nuxt-example/pages/docs/[[slug]].vue (1)
1-58: LGTM!apps/nuxt-example/pages/index.vue (1)
1-12: LGTM!apps/nuxt-example/server/api/docs.get.ts (1)
1-21: LGTM!apps/nuxt-example/tsconfig.json (1)
1-3: LGTM!apps/sveltekit-example/package.json (1)
1-28: LGTM!apps/nuxt-example/server/middleware/agent-readability.ts (1)
6-9: ⚡ Quick winReplace the double-cast with
satisfiesto enable proper type checking.
as unknown as AgentReadabilityManifestbypasses structural validation and masks schema drift. Usesatisfies AgentReadabilityManifestinstead to leverage TypeScript's type narrowing while maintaining type safety.Proposed fix
const manifest = { ...manifestJson, version: 1, -} as unknown as AgentReadabilityManifest; +} satisfies AgentReadabilityManifest;apps/sveltekit-example/src/app.css (1)
1-63: LGTM!apps/sveltekit-example/src/app.html (1)
1-2: LGTM!Also applies to: 8-12
apps/sveltekit-example/src/routes/+layout.svelte (1)
1-5: LGTM!apps/sveltekit-example/src/routes/+page.svelte (1)
1-10: LGTM!apps/sveltekit-example/src/routes/docs/[...slug]/+page.svelte (1)
1-43: LGTM!apps/sveltekit-example/src/routes/docs/[...slug].md/+server.ts (1)
5-8: ⚡ Quick winThe suggested fix using
satisfieswill not compile.When
manifestJsonis imported without a type assertion, it's typed asunknown. TypeScript does not allow spreading anunknownvalue and immediately checking it withsatisfiesin the same expression—the spread operand must be a known type first.The double assertion exists because the JSON import lacks explicit typing. Better alternatives:
- Add import assertion (preferred if the JSON file is stable):
import manifestJson from "../../../../static/docs/agent-readability.json" with { type: "json" }; const manifest = { ...manifestJson, version: 1 } as AgentReadabilityManifest;
- Or cast the spread explicitly:
const manifest = { ...(manifestJson as Record<string, unknown>), version: 1 } as AgentReadabilityManifest;The double assertion is a valid (if verbose) workaround when the JSON source cannot be properly typed at import time.
> Likely an incorrect or invalid review comment.apps/sveltekit-example/svelte.config.js (1)
1-21: LGTM!apps/sveltekit-example/tsconfig.json (1)
1-15: LGTM!apps/sveltekit-example/vite.config.ts (1)
1-6: LGTM!
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@packages/leadtype/src/llm/readability.ts`:
- Around line 126-136: normalizeAgentReadabilityManifest currently force-casts
any object and unconditionally overwrites manifest.version, which masks
incompatible or future manifests; update normalizeAgentReadabilityManifest to
validate that manifest is an object, ensure it has a version field equal to
SUPPORTED_MANIFEST_VERSION (throw a clear error if missing or mismatched),
validate required manifest properties/types (matching AgentReadabilityManifest
shape) before returning, and return the original manifest (or a validated
shallow copy) typed as AgentReadabilityManifest instead of blindly setting
version to SUPPORTED_MANIFEST_VERSION; reference the function
normalizeAgentReadabilityManifest, the AgentReadabilityManifest type, and
SUPPORTED_MANIFEST_VERSION when implementing these checks.
🪄 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: 3232242e-1897-4d1e-a1a5-b665185b4d47
⛔ Files ignored due to path filters (4)
apps/example/src/generated/agent-readability.jsonis excluded by!**/generated/**apps/example/src/generated/docs-search-content.jsonis excluded by!**/generated/**apps/example/src/generated/docs-search-index.jsonis excluded by!**/generated/**bun.lockis excluded by!**/*.lock
📒 Files selected for processing (25)
apps/astro-example/src/pages/docs/[...slug].astroapps/astro-example/src/pages/docs/[...slug].md.tsapps/example/server/utils/agent-readability.tsapps/example/src/lib/docs-head.tsapps/next-example/components/search-box.tsxapps/next-example/next.config.mjsapps/next-example/proxy.tsapps/nuxt-example/nuxt.config.tsapps/nuxt-example/package.jsonapps/nuxt-example/server/middleware/agent-readability.tsapps/nuxt-example/server/routes/docs/[...slug].md.tsapps/sveltekit-example/src/app.htmlapps/sveltekit-example/src/lib/source.tsapps/sveltekit-example/src/routes/docs/[...slug].md/+server.tsapps/sveltekit-example/src/routes/docs/[...slug]/+page.server.tsdocs/build/framework-matrix.mdxpackages/leadtype/src/astro/index.tspackages/leadtype/src/framework-adapters.test.tspackages/leadtype/src/index.tspackages/leadtype/src/internal/framework.tspackages/leadtype/src/llm/index.tspackages/leadtype/src/llm/readability.tspackages/leadtype/src/next/index.tspackages/leadtype/src/nuxt/index.tspackages/leadtype/src/search/react.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{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
Addrel="noopener"when usingtarget="_blank"on links
Files:
apps/sveltekit-example/src/app.htmlapps/next-example/components/search-box.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity
Preferunknownoveranywhen 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:
apps/example/src/lib/docs-head.tspackages/leadtype/src/llm/index.tspackages/leadtype/src/llm/readability.tsapps/sveltekit-example/src/routes/docs/[...slug].md/+server.tsapps/next-example/components/search-box.tsxapps/sveltekit-example/src/routes/docs/[...slug]/+page.server.tsapps/astro-example/src/pages/docs/[...slug].md.tsapps/example/server/utils/agent-readability.tsapps/nuxt-example/server/middleware/agent-readability.tsapps/sveltekit-example/src/lib/source.tsapps/nuxt-example/nuxt.config.tspackages/leadtype/src/index.tsapps/nuxt-example/server/routes/docs/[...slug].md.tsapps/next-example/proxy.tspackages/leadtype/src/search/react.tspackages/leadtype/src/astro/index.tspackages/leadtype/src/framework-adapters.test.tspackages/leadtype/src/nuxt/index.tspackages/leadtype/src/internal/framework.tspackages/leadtype/src/next/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
Preferfor...ofloops over.forEach()and indexedforloops
Use optional chaining (?.) and nullish coalescing (??) for safer property access
Prefer template literals over string concatenation
Use destructuring for object and array assignments
Useconstby default,letonly when reassignment is needed, nevervar
Alwaysawaitpromises in async functions - don't forget to use the return value
Useasync/awaitsyntax 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
Removeconsole.log,debugger, andalertstatements from production code
ThrowErrorobjects with descriptive messages, not strings or other values
Usetry-catchblocks 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 useeval()or assign directly todocument.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:
apps/example/src/lib/docs-head.tspackages/leadtype/src/llm/index.tspackages/leadtype/src/llm/readability.tsapps/sveltekit-example/src/routes/docs/[...slug].md/+server.tsapps/next-example/components/search-box.tsxapps/sveltekit-example/src/routes/docs/[...slug]/+page.server.tsapps/astro-example/src/pages/docs/[...slug].md.tsapps/example/server/utils/agent-readability.tsapps/nuxt-example/server/middleware/agent-readability.tsapps/sveltekit-example/src/lib/source.tsapps/nuxt-example/nuxt.config.tspackages/leadtype/src/index.tsapps/nuxt-example/server/routes/docs/[...slug].md.tsapps/next-example/proxy.tspackages/leadtype/src/search/react.tspackages/leadtype/src/astro/index.tspackages/leadtype/src/framework-adapters.test.tspackages/leadtype/src/nuxt/index.tspackages/leadtype/src/internal/framework.tspackages/leadtype/src/next/index.ts
**/index.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Avoid barrel files (index files that re-export everything)
Files:
packages/leadtype/src/llm/index.tspackages/leadtype/src/index.tspackages/leadtype/src/astro/index.tspackages/leadtype/src/nuxt/index.tspackages/leadtype/src/next/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 thekeyprop 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
AvoiddangerouslySetInnerHTMLunless absolutely necessary
Use proper image components (e.g., Next.js<Image>) over<img>tags
Use Next.js<Image>component for images
Usenext/heador 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 ofReact.forwardRefin React 19+
Files:
apps/next-example/components/search-box.tsx
**/*.{test,spec}.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{test,spec}.{js,ts,jsx,tsx}: Write assertions insideit()ortest()blocks
Avoid done callbacks in async tests - use async/await instead
Don't use.onlyor.skipin committed code
Keep test suites reasonably flat - avoid excessivedescribenesting
Files:
packages/leadtype/src/framework-adapters.test.ts
🪛 LanguageTool
docs/build/framework-matrix.mdx
[style] ~52-~52: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ch/reactin client search components. UsecreateDocsProxy()` when a Next Proxy o...
(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
🔇 Additional comments (23)
apps/astro-example/src/pages/docs/[...slug].astro (1)
1-68: LGTM!apps/astro-example/src/pages/docs/[...slug].md.ts (1)
1-14: LGTM!apps/example/server/utils/agent-readability.ts (1)
3-4: LGTM!Also applies to: 15-16
apps/example/src/lib/docs-head.ts (1)
4-4: LGTM!Also applies to: 8-8
apps/next-example/components/search-box.tsx (1)
1-28: LGTM!apps/next-example/next.config.mjs (1)
1-20: LGTM!apps/next-example/proxy.ts (1)
1-19: LGTM!apps/nuxt-example/nuxt.config.ts (1)
1-6: LGTM!apps/nuxt-example/server/middleware/agent-readability.ts (1)
1-14: LGTM!apps/nuxt-example/server/routes/docs/[...slug].md.ts (1)
1-12: LGTM!apps/sveltekit-example/src/app.html (1)
1-12: LGTM!apps/sveltekit-example/src/lib/source.ts (1)
1-22: LGTM!apps/sveltekit-example/src/routes/docs/[...slug].md/+server.ts (1)
1-12: LGTM!apps/sveltekit-example/src/routes/docs/[...slug]/+page.server.ts (1)
1-25: LGTM!docs/build/framework-matrix.mdx (1)
1-127: LGTM!packages/leadtype/src/astro/index.ts (1)
1-64: LGTM!packages/leadtype/src/framework-adapters.test.ts (1)
1-225: LGTM!packages/leadtype/src/index.ts (1)
39-39: LGTM!packages/leadtype/src/internal/framework.ts (1)
1-205: LGTM!packages/leadtype/src/llm/index.ts (1)
76-76: LGTM!packages/leadtype/src/next/index.ts (1)
1-6: LGTM!Also applies to: 48-54, 142-204
packages/leadtype/src/nuxt/index.ts (1)
1-100: LGTM!packages/leadtype/src/search/react.ts (1)
1-128: LGTM!
| export function normalizeAgentReadabilityManifest( | ||
| manifest: unknown | ||
| ): AgentReadabilityManifest { | ||
| if (typeof manifest !== "object" || manifest === null) { | ||
| throw new Error("leadtype: agent-readability manifest must be an object."); | ||
| } | ||
| return { | ||
| ...manifest, | ||
| version: SUPPORTED_MANIFEST_VERSION, | ||
| } as AgentReadabilityManifest; | ||
| } |
There was a problem hiding this comment.
Prevent version masking and invalid manifest coercion in normalization.
normalizeAgentReadabilityManifest currently overwrites manifest.version to 1 and force-casts arbitrary objects. That can silently accept incompatible/future manifest payloads and fail later in harder-to-debug paths.
Proposed fix
export function normalizeAgentReadabilityManifest(
manifest: unknown
): AgentReadabilityManifest {
- if (typeof manifest !== "object" || manifest === null) {
+ if (
+ typeof manifest !== "object" ||
+ manifest === null ||
+ Array.isArray(manifest)
+ ) {
throw new Error("leadtype: agent-readability manifest must be an object.");
}
+ const candidate = manifest as Record<string, unknown>;
+ const version = candidate.version;
+ if (
+ version !== undefined &&
+ version !== SUPPORTED_MANIFEST_VERSION
+ ) {
+ throw new Error(
+ `leadtype: agent-readability manifest version ${String(version)} is not supported (expected ${SUPPORTED_MANIFEST_VERSION}).`
+ );
+ }
return {
- ...manifest,
+ ...candidate,
version: SUPPORTED_MANIFEST_VERSION,
} as AgentReadabilityManifest;
}🤖 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/llm/readability.ts` around lines 126 - 136,
normalizeAgentReadabilityManifest currently force-casts any object and
unconditionally overwrites manifest.version, which masks incompatible or future
manifests; update normalizeAgentReadabilityManifest to validate that manifest is
an object, ensure it has a version field equal to SUPPORTED_MANIFEST_VERSION
(throw a clear error if missing or mismatched), validate required manifest
properties/types (matching AgentReadabilityManifest shape) before returning, and
return the original manifest (or a validated shallow copy) typed as
AgentReadabilityManifest instead of blindly setting version to
SUPPORTED_MANIFEST_VERSION; reference the function
normalizeAgentReadabilityManifest, the AgentReadabilityManifest type, and
SUPPORTED_MANIFEST_VERSION when implementing these checks.
There was a problem hiding this comment.
No new issues. Reviewed the following changes:
- Replaced the open-coded
{ ...manifestJson, version: 1 } as unknown as AgentReadabilityManifestcast in every dogfood with a new publicnormalizeAgentReadabilityManifesthelper (validates the input is a non-null object, stamps the supported version, and is re-exported fromleadtypeandleadtype/llm); dropped the unused internalnormalizeManifest. - Wrapped
createDocsProxy's innerfetch()in try/catch so network failures degrade to the missing-markdown fallback; updatedapps/next-example/proxy.tsmatcher to"/docs/:path((?!.*\\.md$).*)"so.mdpaths fall through to Next's static asset serving instead of recursing back into the proxy. Added aframework-adapters.test.tscase for the fetch-failure path. - Removed Astro
createDocsEndpoint's bespokecontext.params?.slugshort-circuit (which dropped request headers and unconditionally treated any slug as a markdown request); routing is fully delegated to the sharedcreateAgentArtifactHandler. Added an Astrositemap.mdregression test. - Reworked React
useLeadtypeSearch's stale-result protection to use a monotonically incrementinglatestRequestIdRef, matching Vue/Svelte; overlapping requests with identical query strings no longer corrupt each other. - Hardened
nuxt/eventToRequest: drops the deadname.toLowerCase()fallback inheaderValue, takes the first comma-separated segment ofhost, and validatesx-forwarded-prototo"https"or"http"before constructing the URL. - Resolved dogfood path-resolution issues by deriving paths from
import.meta.url(Nextnext.config.mjstypeTableBasePath; SvelteKitsource.tsfixtureRootwith cwd-relative + module-relative candidates). - Added accessibility (
role="status"/aria-live="polite") to the Astro and Next dogfood search status spans, a default<title>to the SvelteKitapp.html, and an error-fallbackstatus.textContent = "error"in the Astro search handler. - Annotated
apps/sveltekit-example/.../+page.server.tsloadwithPageServerLoad; markedTanStack StartandFumadocsrows asPlannedindocs/build/framework-matrix.mdx; dropped the unused@nuxtjs/mdcMDC plugin wiring fromapps/nuxt-example/nuxt.config.tsand its dependency.
Claude Opus | 𝕏

Summary
Verification
bun testbun test packages/leadtype/src/framework-adapters.test.tsbun --filter leadtype testbun --filter leadtype check-typesbun --filter leadtype buildbun --filter next-example check-typesbun --filter astro-example check-typesbun --filter sveltekit-example check-typesbun --filter nuxt-example check-typesbun --filter next-example buildbun --filter astro-example buildbun --filter sveltekit-example buildbun --filter nuxt-example buildbun x ultracite check packages/leadtype/src docs apps/next-example apps/astro-example apps/sveltekit-example apps/nuxt-example examples/shared-docs .gitignore biome.jsonc