Repository navigation
Add Git-backed OpenAPI API references - #147
Conversation
Import a single-file OpenAPI 3.0/3.1 spec from the connected repository, publish one docs page per operation plus a landing page under /docs/<mount>, a lazily loaded "Try it" playground, and /openapi.json. Adds Tabs, Table, Collapsible, NativeSelect and InlineCode primitives. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adopt the site caching work (#141): API reference reads are cached under docs:<team> and invalidated with the docs events (sites and edge); the OpenAPI migration is renumbered to 0012 after main's 0011. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d8c25800de
ℹ️ 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".
| if (!isObject(operation)) continue; | ||
| const at = ["paths", path, method]; | ||
| if (isHidden(operation)) { | ||
| delete served.paths?.[path]?.[method]; |
There was a problem hiding this comment.
Filter hidden operations from referenced path items
When an OpenAPI 3.1 path references a components.pathItems entry containing an x-internal or x-excluded operation, served.paths[path] is still just the $ref, so deleting the method here does nothing. The published /openapi.json therefore still exposes the hidden operation and its details. Remove hidden operations from the referenced path item or rewrite the exported reference.
Useful? React with 👍 / 👎.
| }); | ||
| if ( | ||
| !binding || | ||
| binding.generation !== input.generation || |
There was a problem hiding this comment.
Fence API activation against newer syncs
If a newer Git sync is queued and activates while this import is still processing an older commit, the binding's connection generation and API settings can remain unchanged. This check then accepts the stale import, allowing it to replace the newer live spec. Also fence activation against the sync request generation or current branch head.
Useful? React with 👍 / 👎.
| </div> | ||
| </aside> | ||
| <article className="flex min-w-0 flex-col gap-8"> | ||
| <details className="@3xl:hidden"> |
There was a problem hiding this comment.
Use the themed Collapsible for mobile navigation
On mobile, this raw <details> is the docs navigation control outside the page-builder primitives; the available Collapsible primitive supplies the themed typography, focus treatment, and disclosure indicator. Use that primitive here to keep the public-site control consistent with the required UI pattern.
AGENTS.md reference: AGENTS.md:L35-L37
Useful? React with 👍 / 👎.
… built Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in panel after syncs - A path whose only operations are x-internal/x-excluded no longer leaves an empty entry in the served spec, so its name stays private. - The API reference panel in the GitHub dialog reloads when a sync starts or finishes, instead of showing the status from when the dialog opened. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sidebar Every docs page rendered every endpoint in the sidebar twice (desktop and mobile), in both the HTML and the RSC payload. With CourseLit's 213 operations each page was ~950 KB and took 2-4 s to render; eight at once exhausted the 256 MB heap on QA and the sites pod restarted (502s). Now the group holding the current page lists its endpoints and every other group links to its section on the API landing page. The same CourseLit page is ~330 KB and renders in ~0.1 s; all 213 pages load with no errors at 8 concurrent requests under a 256 MB heap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… spec is silent A spec with no `security` on the operation or at the root says nothing about authentication. The operation page showed "No authentication required.", which is wrong for APIs like CourseLit's that require auth but don't describe it. The slice now stores `security: null` in that case and the page omits the Authentication section; an explicit `security: []` still reads as no authentication. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The landing page's openapi.json button forced a download. It now opens the hosted spec in a new tab, so its URL (what tools import) is visible; the file can still be saved from there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Next 16.3.x never releases the AbortSignal.any composite that "use cache" creates for each fill (vercel/next.js#97776), so every rendered route stays reachable. A 213-endpoint API reference exhausted the 256 MB heap on QA in minutes. 16.4.3 releases it. Locally under a 128 MB heap: 639 fresh renders plus 852 cached requests, all 200; heap 136 MB with 2 AbortControllers retained (16.3.4: crashed, 457 MB and 2,154 retained). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hem all Bring back expandable tag groups for the whole API reference while keeping pages small: - The current group's endpoints ship with the page (as data, not markup). The other groups load once from the public api-reference endpoint the first time the pointer, focus or a click reaches the sidebar; without JavaScript they link to their section on the landing page. - The docs sidebar is rendered once instead of twice (desktop column plus a mobile copy). Narrow layouts show it behind a CSS-only "Browse articles" toggle. CourseLit's 213-operation reference: operation pages 334 KB -> 240 KB; a docs article 138 KB -> 113 KB; MediaLit's upload page 209 KB -> 168 KB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Titles that fall back to "METHOD /path" have no spaces, so the operation page's heading ran under the Try it column and sidebar entries were clipped. Paths now break after each "/", with overflow-wrap as the fallback. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The operation page's title used the Header1 size, which is too large for long endpoint paths. It's now the page's h1 at the theme's Header2 size, a step above the Header3 section headings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ummary
Without a summary, operations were titled "METHOD /path", repeating the
method next to its badge in the sidebar, heading and landing page. Titles
now follow Fumadocs (and Redoc): the operation's summary, then the path
item's summary, then the operationId in words ("deleteMedia" -> "Delete
Media"), then the bare path. The browser tab title keeps the method for a
bare path, so GET and POST /x stay distinguishable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Like Fumadocs, the operation page now has a request block under the description: server URL, method and path, rendered as plain HTML. "Try it" loads the playground (prefetched on idle or intent, as before) and opens its form inline below the bar; the bar's button then sends the request, and a close button hides the form and returns focus. The right column keeps the code samples and response examples; the method/path line above the title is gone since the panel shows it. Server fields start collapsed. Checked in all six themes, light and dark, and at phone width. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The request panel shows its form right away, like Fumadocs: method, path and Send, then collapsed rows (Server with its URL, Authentication, each parameter location, Body). The page HTML has the same collapsed rows and the form mounts into them after hydration, so nothing shifts. Send opens the rows with empty required inputs and focuses the first one. - API landing: one card per tag group with a row per endpoint instead of a card per endpoint; a bare-path title isn't repeated next to the path. - Descriptions no longer add the text renderer's outer block margins. - page-builder Section clips with overflow-clip, which isn't a scroll container, so docs sidebars and the code panel stick again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Operation pages now offer cURL, JavaScript, Python, Go, Java and C#, like Fumadocs. Each uses only its standard library (net/http, java.net.http, System.Net.Http) and covers JSON, form and multipart bodies; Java points to a library for multipart, which the JDK can't build. Tab padding is tighter so the six tabs fit the code column without scrolling. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rows aligned to the top with a fixed nudge, which only centred the badge for one line height. They now align on the text baseline, so the badge matches the title's first line in every theme. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lighthouse accessibility on API pages goes from 88-90 to 96-97: - API pages: section headings are h2 (sub-headings h3) under the h1, at the same sizes as before; nothing skips a level. - Docs search: the input is a combobox, so its aria-expanded is valid. - page-builder: the Drawer puts trigger props on the trigger button instead of a wrapping span, and footer column titles are h2. Remaining: the themes' destructive red fails 4.5:1 against badge text in several presets; that needs a palette decision. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lighthouse SEO flagged the landing page for having none. It uses the spec's description, else "<API title>: <n> endpoints." Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closes #26.
Summary
A team's OpenAPI spec (JSON or YAML, 3.0 or 3.1) lives in its connected GitHub repo. FrontLit imports it on every sync and publishes an API Reference under
/docs/<mount>: a landing page, one page per operation, and the raw spec at/openapi.json. Design and decisions:docs/openapi-support.md.Import and admin (API, web)
x-internal/x-excludedhide operations from pages, search and the served/openapi.json(empty paths are dropped too).RESERVED_ROUTE); a URL path that clashes with existing docs is rejected (API_MOUNT_CONFLICT).operationIdin words, then the bare path.x-codeSamples.Public site (sites)
security: [])./openapi.jsonopens in a new tab.Also in this PR
apps/sitesupgrades Next.js to 16.4.3: 16.3.x leaks every rendered route undercacheComponents+"use cache"('use cache' fill composites (AbortSignal.any) are never released since 16.3 —renderSignalsource removed, pinned ingcPersistentSignals, retains completed requests (production OOM) vercel/next.js#97776). A 213-endpoint reference exhausted the QA sites pod's heap within minutes.Tabs,Table,Collapsible,NativeSelect,InlineCodeprimitives;Sectionusesoverflow: clipso sticky sidebars stick again;Drawertrigger ARIA and footer heading levels fixed (changesets included).Testing
API: 104 files, 764 tests (MediaLit's live integration tests need network access). Sites: 66 tests. page-builder: 60 tests. Typecheck and lint pass; CI green.
Local browser smoke (MediaLit fork,
docs-sitetest repo): connect, preview, publish, URL-path clash, push-triggered sync, broken spec with diagnostics while the last good version stays live, restore, reserved route, docs linking into the API, all six themes light and dark, phone width.QA (
openapi-8784af1, CourseLit's 213-operation spec fromplatform-based-rewrite): connect via docs setup PR, publish, request panel and validation, sidebar groups, six code tabs, search,/openapi.jsonequals the committed spec (304 on revalidation), sitemap.QA load test: 213 pages × 3 passes + 213 fresh renders at 8 concurrent: all 852 requests 200, no restarts, sites pod memory flat around 250 Mi (512 Mi limit).
PageSpeed Insights on QA:
SEO is low because the test site is hidden from search engines; the landing page's missing meta description is fixed in
574ade4(built, not yet on QA: the QA instance was stopped).Follow-ups
destructivered is below 4.5:1 against badge text in several presets (Classic, Midnight, Neo-Brutalism, most dark modes).OPTIONSwith 401) before "Send" works against them.security(94 operations declaresecurity: []).🤖 Generated with Claude Code