From 42dbf08f5ab022d0c49fb4922644843a6f329bd0 Mon Sep 17 00:00:00 2001 From: Rishabh Date: Thu, 14 May 2026 13:56:38 +0530 Subject: [PATCH 1/4] feat: add redirects config in chronicle.yaml Support from/to redirect pairs with optional permanent flag. Checked before all other routes in route resolver. 301 for permanent, 302 for temporary (default). Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/chronicle/src/lib/route-resolver.ts | 11 ++++++++++- packages/chronicle/src/types/config.ts | 8 ++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/chronicle/src/lib/route-resolver.ts b/packages/chronicle/src/lib/route-resolver.ts index 59fa99c3..2a3ca168 100644 --- a/packages/chronicle/src/lib/route-resolver.ts +++ b/packages/chronicle/src/lib/route-resolver.ts @@ -13,7 +13,7 @@ export const RouteType = { export type RouteType = (typeof RouteType)[keyof typeof RouteType] export type Route = - | { type: typeof RouteType.Redirect; to: string; status: 302 } + | { type: typeof RouteType.Redirect; to: string; status: 301 | 302 } | { type: typeof RouteType.DocsIndex; version: VersionContext } | { type: typeof RouteType.DocsPage; version: VersionContext; slug: string[] } | { type: typeof RouteType.ApiIndex; version: VersionContext } @@ -45,6 +45,15 @@ export function resolveRoute( pathname: string, config: ChronicleConfig, ): Route { + const redirect = config.redirects?.find((r) => r.from === pathname) + if (redirect) { + return { + type: RouteType.Redirect, + to: redirect.to, + status: redirect.permanent ? 301 : 302, + } + } + const parts = pathname.split('/').filter(Boolean) const version = resolveVersionFromUrl(pathname, config) const remainder = diff --git a/packages/chronicle/src/types/config.ts b/packages/chronicle/src/types/config.ts index fbc79b55..1c7b6136 100644 --- a/packages/chronicle/src/types/config.ts +++ b/packages/chronicle/src/types/config.ts @@ -131,6 +131,12 @@ const RESERVED_ROUTE_SEGMENTS = [ 'sitemap.xml', ] as const +const redirectSchema = z.object({ + from: z.string(), + to: z.string(), + permanent: z.boolean().optional(), +}) + export const chronicleConfigSchema = z .object({ site: siteSchema, @@ -144,6 +150,7 @@ export const chronicleConfigSchema = z navigation: navigationSchema.optional(), search: searchSchema.optional(), api: z.array(apiSchema).optional(), + redirects: z.array(redirectSchema).optional(), analytics: analyticsSchema.optional(), telemetry: telemetrySchema.optional(), }) @@ -225,6 +232,7 @@ export type SocialLink = z.infer export type SearchConfig = z.infer export type ApiConfig = z.infer export type ApiServerConfig = z.infer +export type RedirectConfig = z.infer export type ApiAuthConfig = z.infer export type AnalyticsConfig = z.infer export type GoogleAnalyticsConfig = z.infer From bb75aa0cce6af2aa3a662a9e9c3075b0c3ab429a Mon Sep 17 00:00:00 2001 From: Rishabh Date: Thu, 14 May 2026 14:46:30 +0530 Subject: [PATCH 2/4] refactor: use StatusCodes and 307/308 for redirects - 307 Temporary Redirect (default) - 308 Permanent Redirect (permanent: true) - Update route-resolver tests Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/chronicle/src/lib/route-resolver.test.ts | 6 +++--- packages/chronicle/src/lib/route-resolver.ts | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/chronicle/src/lib/route-resolver.test.ts b/packages/chronicle/src/lib/route-resolver.test.ts index ccc2ba31..d1bc9d57 100644 --- a/packages/chronicle/src/lib/route-resolver.test.ts +++ b/packages/chronicle/src/lib/route-resolver.test.ts @@ -60,7 +60,7 @@ describe('resolveRoute — root', () => { expect(resolveRoute('/', singleContent())).toEqual({ type: RouteType.Redirect, to: '/docs', - status: 302, + status: 307, }) }) @@ -75,7 +75,7 @@ describe('resolveRoute — root', () => { expect(resolveRoute('/', multiContentNoLanding())).toEqual({ type: RouteType.Redirect, to: '/docs', - status: 302, + status: 307, }) }) @@ -83,7 +83,7 @@ describe('resolveRoute — root', () => { expect(resolveRoute('/v2', versioned())).toEqual({ type: RouteType.Redirect, to: '/v2/docs', - status: 302, + status: 307, }) }) diff --git a/packages/chronicle/src/lib/route-resolver.ts b/packages/chronicle/src/lib/route-resolver.ts index 2a3ca168..fc3857a2 100644 --- a/packages/chronicle/src/lib/route-resolver.ts +++ b/packages/chronicle/src/lib/route-resolver.ts @@ -1,3 +1,4 @@ +import { StatusCodes } from 'http-status-codes' import type { ChronicleConfig } from '@/types' import { getLatestContentRoots, getVersionContentRoots } from './config' import { type VersionContext, resolveVersionFromUrl } from './version-source' @@ -13,7 +14,7 @@ export const RouteType = { export type RouteType = (typeof RouteType)[keyof typeof RouteType] export type Route = - | { type: typeof RouteType.Redirect; to: string; status: 301 | 302 } + | { type: typeof RouteType.Redirect; to: string; status: StatusCodes.TEMPORARY_REDIRECT | StatusCodes.PERMANENT_REDIRECT } | { type: typeof RouteType.DocsIndex; version: VersionContext } | { type: typeof RouteType.DocsPage; version: VersionContext; slug: string[] } | { type: typeof RouteType.ApiIndex; version: VersionContext } @@ -50,7 +51,7 @@ export function resolveRoute( return { type: RouteType.Redirect, to: redirect.to, - status: redirect.permanent ? 301 : 302, + status: redirect.permanent ? StatusCodes.PERMANENT_REDIRECT : StatusCodes.TEMPORARY_REDIRECT, } } @@ -74,7 +75,7 @@ export function resolveRoute( return { type: RouteType.Redirect, to: `${version.urlPrefix}/${dirs[0]}`, - status: 302, + status: StatusCodes.TEMPORARY_REDIRECT, } } From 36a40387a446adecb0a717b4479a36eb449a287c Mon Sep 17 00:00:00 2001 From: Rishabh Date: Thu, 14 May 2026 14:59:13 +0530 Subject: [PATCH 3/4] docs: add redirects section to configuration reference Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/content/docs/configuration.mdx | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/content/docs/configuration.mdx b/docs/content/docs/configuration.mdx index 4bf34b74..7b5352d9 100644 --- a/docs/content/docs/configuration.mdx +++ b/docs/content/docs/configuration.mdx @@ -99,6 +99,13 @@ api: header: Authorization placeholder: "Bearer token" +redirects: + - from: /old-page + to: /docs/new-page + - from: /legacy/api + to: /apis + permanent: true + analytics: enabled: true googleAnalytics: @@ -319,6 +326,27 @@ api: | `auth.header` | `string` | Header name for auth token | | `auth.placeholder` | `string` | Placeholder text in auth input | +### redirects + +URL redirects for migrating old routes to new ones. Checked before all other routes. + +```yaml +redirects: + - from: /old-page + to: /docs/getting-started + - from: /legacy/api-docs + to: /apis + permanent: true +``` + +| Field | Type | Description | Default | +|-------|------|-------------|---------| +| `from` | `string` | Old URL path to redirect from | — | +| `to` | `string` | New URL path to redirect to | — | +| `permanent` | `boolean` | `true` for 308 (permanent), `false` for 307 (temporary) | `false` | + +Use `permanent: true` when the old URL should never be used again — search engines and browsers will cache the redirect. + ### analytics Analytics integration for tracking page views. From c498a88d7218e2966bc94b55af0fe601d9e3424d Mon Sep 17 00:00:00 2001 From: Rishabh Date: Thu, 14 May 2026 15:01:10 +0530 Subject: [PATCH 4/4] docs: add features overview page Covers content, API reference, navigation, themes, SEO/AI, DX, redirects, sorting, markdown URLs, playground, and health endpoints. Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/content/docs/features.mdx | 145 +++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 docs/content/docs/features.mdx diff --git a/docs/content/docs/features.mdx b/docs/content/docs/features.mdx new file mode 100644 index 00000000..aacdad16 --- /dev/null +++ b/docs/content/docs/features.mdx @@ -0,0 +1,145 @@ +--- +title: Features +description: Overview of Chronicle features +order: 2 +--- + +# Features + +Chronicle is a self-hosted documentation platform built with Vite + Nitro. + +## Content + +- **MDX support** — write documentation in MDX with React component embedding +- **Frontmatter** — `title`, `description`, `order`, `icon`, `lastModified` +- **Directory metadata** — `meta.json` for folder titles, ordering, and sidebar config +- **Remark plugins** — directives, admonitions, image resolution, link resolution, mermaid, reading time +- **Syntax highlighting** — powered by Shiki via Apsara CodeBlock +- **Versioning** — multiple documentation versions with URL-based routing + +## API Reference + +- **OpenAPI / Swagger support** — auto-generates API reference pages from specs (OpenAPI 3.x and Swagger 2.0) +- **Read-only overview** — field names, types, required badges, examples, response schemas +- **Playground dialog** — test requests with editable fields, JSON body editor, auth switching +- **Auth types** — API Key, Bearer Token, Basic Auth (auto-detected from spec `securitySchemes`) +- **Code snippets** — cURL, Python, Go, TypeScript with language switcher +- **Response panel** — status code tabs with JSON syntax highlighting +- **`.md` export** — every API endpoint has a `.md` URL with full documentation + +## Navigation + +- **Sidebar** — auto-generated from file structure, configurable via `meta.json` +- **Breadcrumbs** — shows path hierarchy for docs and API pages +- **Prev/Next** — arrow navigation between pages and API endpoints +- **Search** — full-text search with SQLite FTS5 across titles, headings, and body content +- **Folder sorting** — via `order` in `meta.json` +- **Page sorting** — via `order` in frontmatter or `pages` array in `meta.json` + +## Themes + +- **Default theme** — sidebar + content layout with sub-navigation bar +- **Paper theme** — book-style single-column with reading progress +- **Dark/light mode** — system preference or manual toggle + +## SEO & AI + +- **Meta tags** — auto-generated title, description, Open Graph, Twitter Card +- **Sitemap** — auto-generated `sitemap.xml` +- **robots.txt** — auto-generated +- **llms.txt** — AI-discoverable documentation index +- **`.md` URLs** — every page (docs and API) has a markdown URL for AI tools +- **Open in AI** — copy as markdown, open in ChatGPT or Claude + +## Developer Experience + +- **CLI** — `chronicle dev`, `chronicle build`, `chronicle start` +- **Hot reload** — instant updates during development +- **Monorepo support** — works as a package in monorepos +- **Docker support** — containerized deployment +- **Zod-validated config** — `chronicle.yaml` with schema validation + +## Redirects + +Configure URL redirects in `chronicle.yaml` for migrating old routes. + +```yaml +redirects: + - from: /old-page + to: /docs/getting-started + - from: /legacy/api + to: /apis + permanent: true +``` + +- `permanent: false` (default) — 307 temporary redirect +- `permanent: true` — 308 permanent redirect + +See [Configuration](/docs/configuration#redirects) for full reference. + +## Sorting + +### Pages + +Add `order` to frontmatter. Lower numbers appear first. + +```mdx +--- +title: Introduction +order: 1 +--- +``` + +Or use `pages` array in `meta.json`: + +```json +{ + "pages": ["introduction", "installation", "configuration"] +} +``` + +### Folders + +Add `order` to the folder's `meta.json`: + +```json +{ + "title": "Getting Started", + "order": 1 +} +``` + +Folder sorting is controlled only by `meta.json` `order`. The index page frontmatter `order` does not affect folder position. + +## Markdown URLs + +Every page has a `.md` URL that returns raw markdown: + +- **Docs pages** — `/{slug}.md` returns the raw MDX content +- **API endpoints** — `/apis/{spec}/{operationId}.md` generates markdown with parameters, examples, responses, and cURL + +```bash +curl https://docs.example.com/docs/getting-started.md +curl https://docs.example.com/apis/petstore/findPetsByStatus.md +``` + +The "Open in AI" dropdown uses these URLs to copy markdown, open in ChatGPT, or open in Claude. + +## API Reference Page + +### Overview + +The read-only overview shows endpoint title, method badge, path, authorisation fields, query/path parameters, request body, and response schemas. The right column has code snippets and response JSON. + +### Playground + +Click **Test request** in the navbar to open the playground dialog with editable fields, JSON body editor, auth type switching, and live response with status and timing. + +### View Documentation + +If the OpenAPI spec has `externalDocs`, a **View documentation** button appears in the navbar. + +## Health & Readiness + +- `GET /api/health` — liveness probe, always returns `200` +- `GET /api/ready` — readiness probe, returns `200` when search index is built, `503` otherwise