mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 18:05:11 +03:00
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Feature, bug fix. Part 3 of 6 in a stack that splits the library redesign into reviewable pieces. ## What is the current behavior? An agent can already fetch any guide as Markdown, but has no way to find out what guides exist: the entry point is a rendered React page. The exporter also fails quietly in ways that ship wrong output rather than failing the build: - An unknown component silently unwraps to its children, so a component rename drops its rendered content. - A registry item that cannot be read produces a page with no file listing. - A link to a missing page produces a 404 URL. - An unrecognized install framework produces a plausible command for the wrong CLI. - Only absolute `/library/docs` links are rewritten, so in-page anchors and sibling links break in the export. ## What is the new behavior? `/library` negotiates Markdown the same way the guides do — `Accept: text/markdown`, or an explicit `/library/index.md` — and returns a categorized catalog with every block, its description, its framework variants, and a link to each guide's Markdown. `config/library.ts` is the single catalog description the generator reads, and a test ties it to the content directory in both directions: a guide cannot be added without a catalog entry, or listed without a guide. Each quiet failure above now throws, and links resolve against the page they appear on and are checked against the set of published documents. ```bash curl -H 'Accept: text/markdown' https://supabase.com/library ``` ## Additional context `config/library.ts` also carries the category and preview metadata the redesigned homepage consumes in the last PR of the stack; here it is exercised by the Markdown index and its test. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added a browsable library catalog covering categories, blocks, starter apps, and supported frameworks. - Added Markdown versions of the library homepage and documentation for compatible tools and workflows. - Added framework-aware links and expanded registry information, including dependencies and source details. - Markdown requests now work for the homepage and documentation, while browser requests continue receiving HTML. - **Bug Fixes** - Improved document link handling, metadata validation, slug consistency, and detection of duplicate or missing documentation entries. - **Tests** - Added coverage for catalog routes, Markdown generation, homepage negotiation, document parsing, and framework-specific links. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
212 lines
6.0 KiB
TypeScript
212 lines
6.0 KiB
TypeScript
import { componentPages, mcpBlocks, oauthBlocks, platformBlocks } from './docs'
|
||
|
||
export const libraryCategories = [
|
||
{
|
||
name: 'Starter apps',
|
||
slug: 'starter-apps',
|
||
description: 'Complete starting points for your next product.',
|
||
},
|
||
{
|
||
name: 'Authentication',
|
||
slug: 'authentication',
|
||
description: 'Sign-in, sessions, and account management.',
|
||
},
|
||
{ name: 'Database', slug: 'database', description: 'Connect your interface to Postgres data.' },
|
||
{ name: 'Storage', slug: 'storage', description: 'Upload files with Supabase Storage.' },
|
||
{ name: 'Realtime', slug: 'realtime', description: 'Build experiences that stay in sync.' },
|
||
{
|
||
name: 'Messaging',
|
||
slug: 'messaging',
|
||
description: 'Bring conversations into your application.',
|
||
},
|
||
{ name: 'AI & APIs', slug: 'ai-apis', description: 'Connect agents to your application.' },
|
||
{
|
||
name: 'Application foundations',
|
||
slug: 'application-foundations',
|
||
description: 'Connect and extend your Supabase project.',
|
||
},
|
||
] as const
|
||
|
||
export type LibraryCategory = (typeof libraryCategories)[number]['name']
|
||
export type CatalogPreviewKind =
|
||
| 'auth'
|
||
| 'social'
|
||
| 'consent'
|
||
| 'avatar'
|
||
| 'table'
|
||
| 'storage'
|
||
| 'cursors'
|
||
| 'editor'
|
||
| 'flow'
|
||
| 'avatars'
|
||
| 'chat'
|
||
| 'mcp'
|
||
| 'agents'
|
||
| 'dashboard'
|
||
| 'client'
|
||
|
||
export type LibraryBlock = {
|
||
slug: string
|
||
title: string
|
||
description: string
|
||
category: LibraryCategory
|
||
preview: CatalogPreviewKind
|
||
tags: string[]
|
||
href: string
|
||
supportedFrameworks?: string[]
|
||
frameworkLabel?: string
|
||
external?: boolean
|
||
}
|
||
|
||
const blockMetadata: Record<string, Pick<LibraryBlock, 'description' | 'category' | 'preview'>> = {
|
||
'password-based-auth': {
|
||
description: 'Sign in and sign up with email and password.',
|
||
category: 'Authentication',
|
||
preview: 'auth',
|
||
},
|
||
'social-auth': {
|
||
description: 'OAuth sign-in flows for popular providers.',
|
||
category: 'Authentication',
|
||
preview: 'social',
|
||
},
|
||
'oauth-consent': {
|
||
description: 'Let users review and approve application access.',
|
||
category: 'Authentication',
|
||
preview: 'consent',
|
||
},
|
||
'current-user-avatar': {
|
||
description: 'Display the signed-in user’s avatar and profile.',
|
||
category: 'Authentication',
|
||
preview: 'avatar',
|
||
},
|
||
'infinite-query': {
|
||
description: 'Fetch and paginate Supabase data as users scroll.',
|
||
category: 'Database',
|
||
preview: 'table',
|
||
},
|
||
dropzone: {
|
||
description: 'Drag-and-drop file uploads with progress tracking.',
|
||
category: 'Storage',
|
||
preview: 'storage',
|
||
},
|
||
'realtime-cursor': {
|
||
description: 'Share live cursor positions across your application.',
|
||
category: 'Realtime',
|
||
preview: 'cursors',
|
||
},
|
||
'realtime-monaco': {
|
||
description: 'Edit code together with a collaborative Monaco editor.',
|
||
category: 'Realtime',
|
||
preview: 'editor',
|
||
},
|
||
'realtime-flow': {
|
||
description: 'Build collaborative diagrams with React Flow.',
|
||
category: 'Realtime',
|
||
preview: 'flow',
|
||
},
|
||
'realtime-avatar-stack': {
|
||
description: 'Show who is online with a live avatar stack.',
|
||
category: 'Realtime',
|
||
preview: 'avatars',
|
||
},
|
||
'realtime-chat': {
|
||
description: 'Send and receive messages in realtime.',
|
||
category: 'Messaging',
|
||
preview: 'chat',
|
||
},
|
||
'mcp-server': {
|
||
description: 'Add a user-scoped MCP server to your product.',
|
||
category: 'AI & APIs',
|
||
preview: 'mcp',
|
||
},
|
||
'headless-app': {
|
||
description: 'Combine auth, consent, and MCP tools into an agent-driven app.',
|
||
category: 'AI & APIs',
|
||
preview: 'agents',
|
||
},
|
||
client: {
|
||
description: 'Set up a Supabase client for your framework.',
|
||
category: 'Application foundations',
|
||
preview: 'client',
|
||
},
|
||
'platform-kit': {
|
||
description: 'Embed Supabase project management in your platform.',
|
||
category: 'Application foundations',
|
||
preview: 'dashboard',
|
||
},
|
||
}
|
||
|
||
// Starter guides share the same documentation routes and layout as individual blocks.
|
||
const starterApps: LibraryBlock[] = [
|
||
{
|
||
title: 'Next.js starter',
|
||
slug: 'nextjs-starter',
|
||
description: 'A Next.js app with cookie-based authentication, TypeScript, and Tailwind CSS.',
|
||
category: 'Starter apps',
|
||
preview: 'auth',
|
||
tags: ['Next.js', 'Authentication', 'TypeScript'],
|
||
href: '/docs/starters/nextjs-starter',
|
||
frameworkLabel: 'Next.js',
|
||
},
|
||
{
|
||
title: 'SaaS starter',
|
||
slug: 'saas-starter',
|
||
description: 'Subscription payments with Stripe, Supabase, and Next.js.',
|
||
category: 'Starter apps',
|
||
preview: 'dashboard',
|
||
tags: ['Next.js', 'Stripe', 'Subscriptions'],
|
||
href: '/docs/starters/saas-starter',
|
||
frameworkLabel: 'Next.js',
|
||
},
|
||
{
|
||
title: 'AI chat app',
|
||
slug: 'ai-chat-app',
|
||
description: 'A conversational app with Next.js, the Vercel AI SDK, and Supabase.',
|
||
category: 'Starter apps',
|
||
preview: 'chat',
|
||
tags: ['Next.js', 'AI', 'Messaging'],
|
||
href: '/docs/starters/ai-chat-app',
|
||
frameworkLabel: 'Next.js',
|
||
},
|
||
{
|
||
title: 'Flutter starter',
|
||
slug: 'flutter-starter',
|
||
description: 'A user management app with authentication, profiles, and file storage.',
|
||
category: 'Starter apps',
|
||
preview: 'avatar',
|
||
tags: ['Flutter', 'Authentication', 'Storage'],
|
||
href: '/docs/starters/flutter-starter',
|
||
frameworkLabel: 'Flutter',
|
||
},
|
||
]
|
||
|
||
export const libraryBlocks: LibraryBlock[] = [
|
||
...starterApps,
|
||
...[
|
||
...componentPages.items,
|
||
...oauthBlocks.items,
|
||
...mcpBlocks.items,
|
||
...platformBlocks.items,
|
||
].map((item) => {
|
||
const slug = item.href!.split('/').pop()!
|
||
return {
|
||
slug,
|
||
title: item.title,
|
||
href: item.href!,
|
||
supportedFrameworks: item.supportedFrameworks,
|
||
tags: item.supportedFrameworks ?? [],
|
||
...blockMetadata[slug],
|
||
}
|
||
}),
|
||
]
|
||
|
||
export function getLibraryBlockHref(block: LibraryBlock, framework?: string) {
|
||
return framework && block.supportedFrameworks?.includes(framework)
|
||
? `/docs/${framework}/${block.slug}`
|
||
: block.href
|
||
}
|
||
|
||
export function getLibraryCategoryHref(category: LibraryCategory) {
|
||
return `/?category=${encodeURIComponent(category)}`
|
||
}
|