@raystack/chronicle 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +425 -9937
- package/package.json +19 -10
- package/src/cli/commands/build.ts +33 -31
- package/src/cli/commands/dev.ts +23 -31
- package/src/cli/commands/init.ts +38 -132
- package/src/cli/commands/serve.ts +41 -55
- package/src/cli/commands/start.ts +20 -31
- package/src/cli/index.ts +14 -14
- package/src/cli/utils/config.ts +58 -30
- package/src/cli/utils/index.ts +3 -3
- package/src/cli/utils/resolve.ts +7 -3
- package/src/cli/utils/scaffold.ts +11 -130
- package/src/components/mdx/code.tsx +10 -1
- package/src/components/mdx/details.module.css +1 -26
- package/src/components/mdx/details.tsx +2 -3
- package/src/components/mdx/image.tsx +5 -34
- package/src/components/mdx/index.tsx +15 -1
- package/src/components/mdx/link.tsx +18 -15
- package/src/components/ui/breadcrumbs.tsx +8 -42
- package/src/components/ui/search.tsx +63 -51
- package/src/lib/api-routes.ts +6 -8
- package/src/lib/config.ts +12 -36
- package/src/lib/head.tsx +49 -0
- package/src/lib/openapi.ts +8 -8
- package/src/lib/page-context.tsx +111 -0
- package/src/lib/remark-strip-md-extensions.ts +14 -0
- package/src/lib/source.ts +139 -63
- package/src/pages/ApiLayout.tsx +33 -0
- package/src/pages/ApiPage.tsx +73 -0
- package/src/pages/DocsLayout.tsx +18 -0
- package/src/pages/DocsPage.tsx +43 -0
- package/src/pages/NotFound.tsx +17 -0
- package/src/server/App.tsx +72 -0
- package/src/server/api/apis-proxy.ts +69 -0
- package/src/server/api/health.ts +5 -0
- package/src/server/api/page/[...slug].ts +18 -0
- package/src/server/api/search.ts +118 -0
- package/src/server/api/specs.ts +9 -0
- package/src/server/build-search-index.ts +117 -0
- package/src/server/entry-client.tsx +88 -0
- package/src/server/entry-server.tsx +102 -0
- package/src/server/routes/llms.txt.ts +21 -0
- package/src/server/routes/og.tsx +75 -0
- package/src/server/routes/robots.txt.ts +11 -0
- package/src/server/routes/sitemap.xml.ts +40 -0
- package/src/server/utils/safe-path.ts +17 -0
- package/src/server/vite-config.ts +133 -0
- package/src/themes/default/Layout.tsx +78 -48
- package/src/themes/default/Page.module.css +44 -0
- package/src/themes/default/Page.tsx +9 -11
- package/src/themes/default/Toc.tsx +25 -39
- package/src/themes/default/index.ts +7 -9
- package/src/themes/paper/ChapterNav.tsx +64 -45
- package/src/themes/paper/Layout.module.css +1 -1
- package/src/themes/paper/Layout.tsx +24 -12
- package/src/themes/paper/Page.module.css +16 -4
- package/src/themes/paper/Page.tsx +56 -63
- package/src/themes/paper/ReadingProgress.tsx +160 -139
- package/src/themes/paper/index.ts +5 -5
- package/src/themes/registry.ts +14 -7
- package/src/types/config.ts +86 -67
- package/src/types/content.ts +5 -21
- package/src/types/globals.d.ts +4 -0
- package/src/types/theme.ts +4 -3
- package/tsconfig.json +2 -3
- package/next.config.mjs +0 -10
- package/source.config.ts +0 -51
- package/src/app/[[...slug]]/layout.tsx +0 -15
- package/src/app/[[...slug]]/page.tsx +0 -106
- package/src/app/api/apis-proxy/route.ts +0 -59
- package/src/app/api/health/route.ts +0 -3
- package/src/app/api/search/route.ts +0 -90
- package/src/app/apis/[[...slug]]/layout.tsx +0 -26
- package/src/app/apis/[[...slug]]/page.tsx +0 -117
- package/src/app/layout.tsx +0 -57
- package/src/app/llms-full.txt/route.ts +0 -18
- package/src/app/llms.txt/route.ts +0 -15
- package/src/app/og/route.tsx +0 -62
- package/src/app/providers.tsx +0 -8
- package/src/app/robots.ts +0 -10
- package/src/app/sitemap.ts +0 -29
- package/src/cli/utils/process.ts +0 -7
- package/src/themes/default/font.ts +0 -6
- /package/src/{app/apis/[[...slug]]/layout.module.css → pages/ApiLayout.module.css} +0 -0
package/src/themes/registry.ts
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
|
-
import type { Theme } from '@/types'
|
|
2
|
-
import { defaultTheme } from './default'
|
|
3
|
-
import { paperTheme } from './paper'
|
|
1
|
+
import type { Theme } from '@/types';
|
|
2
|
+
import { defaultTheme } from './default';
|
|
3
|
+
import { paperTheme } from './paper';
|
|
4
4
|
|
|
5
5
|
const themes: Record<string, Theme> = {
|
|
6
6
|
default: defaultTheme,
|
|
7
|
-
paper: paperTheme
|
|
8
|
-
}
|
|
7
|
+
paper: paperTheme
|
|
8
|
+
};
|
|
9
9
|
|
|
10
10
|
export function getTheme(name?: string): Theme {
|
|
11
|
-
if (!name || !themes[name]) return defaultTheme
|
|
11
|
+
if (!name || !themes[name]) return defaultTheme;
|
|
12
|
+
|
|
13
|
+
return themes[name];
|
|
14
|
+
}
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
export function getThemeConfig(name?: string) {
|
|
17
|
+
if (name === 'paper') {
|
|
18
|
+
return { enableSystem: false, forcedTheme: 'light' };
|
|
19
|
+
}
|
|
20
|
+
return { enableSystem: true };
|
|
14
21
|
}
|
package/src/types/config.ts
CHANGED
|
@@ -1,80 +1,99 @@
|
|
|
1
|
-
|
|
2
|
-
title: string
|
|
3
|
-
description?: string
|
|
4
|
-
url?: string
|
|
5
|
-
logo?: LogoConfig
|
|
6
|
-
theme?: ThemeConfig
|
|
7
|
-
navigation?: NavigationConfig
|
|
8
|
-
search?: SearchConfig
|
|
9
|
-
footer?: FooterConfig
|
|
10
|
-
api?: ApiConfig[]
|
|
11
|
-
llms?: LlmsConfig
|
|
12
|
-
analytics?: AnalyticsConfig
|
|
13
|
-
}
|
|
1
|
+
import { z } from 'zod'
|
|
14
2
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
3
|
+
const logoSchema = z.object({
|
|
4
|
+
light: z.string().optional(),
|
|
5
|
+
dark: z.string().optional(),
|
|
6
|
+
})
|
|
18
7
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
8
|
+
const themeSchema = z.object({
|
|
9
|
+
name: z.enum(['default', 'paper']),
|
|
10
|
+
colors: z.record(z.string(), z.string()).optional(),
|
|
11
|
+
})
|
|
23
12
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
13
|
+
const navLinkSchema = z.object({
|
|
14
|
+
label: z.string(),
|
|
15
|
+
href: z.string(),
|
|
16
|
+
})
|
|
27
17
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
server: ApiServerConfig
|
|
33
|
-
auth?: ApiAuthConfig
|
|
34
|
-
}
|
|
18
|
+
const socialLinkSchema = z.object({
|
|
19
|
+
type: z.string(),
|
|
20
|
+
href: z.string(),
|
|
21
|
+
})
|
|
35
22
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
23
|
+
const navigationSchema = z.object({
|
|
24
|
+
links: z.array(navLinkSchema).optional(),
|
|
25
|
+
social: z.array(socialLinkSchema).optional(),
|
|
26
|
+
})
|
|
40
27
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
28
|
+
const searchSchema = z.object({
|
|
29
|
+
enabled: z.boolean().optional(),
|
|
30
|
+
placeholder: z.string().optional(),
|
|
31
|
+
})
|
|
46
32
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
33
|
+
const apiServerSchema = z.object({
|
|
34
|
+
url: z.string(),
|
|
35
|
+
description: z.string().optional(),
|
|
36
|
+
})
|
|
51
37
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
38
|
+
const apiAuthSchema = z.object({
|
|
39
|
+
type: z.string(),
|
|
40
|
+
header: z.string(),
|
|
41
|
+
placeholder: z.string().optional(),
|
|
42
|
+
})
|
|
56
43
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
44
|
+
const apiSchema = z.object({
|
|
45
|
+
name: z.string(),
|
|
46
|
+
spec: z.string(),
|
|
47
|
+
basePath: z.string(),
|
|
48
|
+
server: apiServerSchema,
|
|
49
|
+
auth: apiAuthSchema.optional(),
|
|
50
|
+
})
|
|
61
51
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
52
|
+
const footerSchema = z.object({
|
|
53
|
+
copyright: z.string().optional(),
|
|
54
|
+
links: z.array(navLinkSchema).optional(),
|
|
55
|
+
})
|
|
66
56
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
57
|
+
const llmsSchema = z.object({
|
|
58
|
+
enabled: z.boolean().optional(),
|
|
59
|
+
})
|
|
71
60
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
61
|
+
const googleAnalyticsSchema = z.object({
|
|
62
|
+
measurementId: z.string(),
|
|
63
|
+
})
|
|
76
64
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
65
|
+
const analyticsSchema = z.object({
|
|
66
|
+
enabled: z.boolean().optional(),
|
|
67
|
+
googleAnalytics: googleAnalyticsSchema.optional(),
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
export const chronicleConfigSchema = z.object({
|
|
71
|
+
title: z.string(),
|
|
72
|
+
description: z.string().optional(),
|
|
73
|
+
url: z.string().optional(),
|
|
74
|
+
content: z.string().optional(),
|
|
75
|
+
preset: z.string().optional(),
|
|
76
|
+
logo: logoSchema.optional(),
|
|
77
|
+
theme: themeSchema.optional(),
|
|
78
|
+
navigation: navigationSchema.optional(),
|
|
79
|
+
search: searchSchema.optional(),
|
|
80
|
+
footer: footerSchema.optional(),
|
|
81
|
+
api: z.array(apiSchema).optional(),
|
|
82
|
+
llms: llmsSchema.optional(),
|
|
83
|
+
analytics: analyticsSchema.optional(),
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
export type ChronicleConfig = z.infer<typeof chronicleConfigSchema>
|
|
87
|
+
export type LogoConfig = z.infer<typeof logoSchema>
|
|
88
|
+
export type ThemeConfig = z.infer<typeof themeSchema>
|
|
89
|
+
export type NavigationConfig = z.infer<typeof navigationSchema>
|
|
90
|
+
export type NavLink = z.infer<typeof navLinkSchema>
|
|
91
|
+
export type SocialLink = z.infer<typeof socialLinkSchema>
|
|
92
|
+
export type SearchConfig = z.infer<typeof searchSchema>
|
|
93
|
+
export type ApiConfig = z.infer<typeof apiSchema>
|
|
94
|
+
export type ApiServerConfig = z.infer<typeof apiServerSchema>
|
|
95
|
+
export type ApiAuthConfig = z.infer<typeof apiAuthSchema>
|
|
96
|
+
export type FooterConfig = z.infer<typeof footerSchema>
|
|
97
|
+
export type LlmsConfig = z.infer<typeof llmsSchema>
|
|
98
|
+
export type AnalyticsConfig = z.infer<typeof analyticsSchema>
|
|
99
|
+
export type GoogleAnalyticsConfig = z.infer<typeof googleAnalyticsSchema>
|
package/src/types/content.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { ReactNode } from 'react'
|
|
2
|
+
import type { TableOfContents } from 'fumadocs-core/toc'
|
|
3
|
+
|
|
4
|
+
export type { Root, Node, Item, Folder, Separator } from 'fumadocs-core/page-tree'
|
|
5
|
+
export type { TOCItemType, TableOfContents } from 'fumadocs-core/toc'
|
|
2
6
|
|
|
3
7
|
export interface Frontmatter {
|
|
4
8
|
title: string
|
|
@@ -12,25 +16,5 @@ export interface Page {
|
|
|
12
16
|
slug: string[]
|
|
13
17
|
frontmatter: Frontmatter
|
|
14
18
|
content: ReactNode
|
|
15
|
-
toc:
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface TocItem {
|
|
19
|
-
title: string
|
|
20
|
-
url: string
|
|
21
|
-
depth: number
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface PageTreeItem {
|
|
25
|
-
type: 'page' | 'folder' | 'separator'
|
|
26
|
-
name: string
|
|
27
|
-
url?: string
|
|
28
|
-
order?: number
|
|
29
|
-
icon?: string
|
|
30
|
-
children?: PageTreeItem[]
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface PageTree {
|
|
34
|
-
name: string
|
|
35
|
-
children: PageTreeItem[]
|
|
19
|
+
toc: TableOfContents
|
|
36
20
|
}
|
package/src/types/theme.ts
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import type { ReactNode } from 'react'
|
|
2
|
+
import type { Root } from 'fumadocs-core/page-tree'
|
|
2
3
|
import type { ChronicleConfig } from './config'
|
|
3
|
-
import type { Page
|
|
4
|
+
import type { Page } from './content'
|
|
4
5
|
|
|
5
6
|
export interface ThemeLayoutProps {
|
|
6
7
|
children: ReactNode
|
|
7
8
|
config: ChronicleConfig
|
|
8
|
-
tree:
|
|
9
|
+
tree: Root
|
|
9
10
|
classNames?: { layout?: string; body?: string; sidebar?: string; content?: string }
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export interface ThemePageProps {
|
|
13
14
|
page: Page
|
|
14
15
|
config: ChronicleConfig
|
|
15
|
-
tree:
|
|
16
|
+
tree: Root
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export interface Theme {
|
package/tsconfig.json
CHANGED
|
@@ -21,10 +21,9 @@
|
|
|
21
21
|
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
22
22
|
"moduleResolution": "bundler",
|
|
23
23
|
"paths": {
|
|
24
|
-
"@/*": ["./src/*"]
|
|
25
|
-
"@/.source/*": ["./.source/*"]
|
|
24
|
+
"@/*": ["./src/*"]
|
|
26
25
|
}
|
|
27
26
|
},
|
|
28
|
-
"include": ["src"
|
|
27
|
+
"include": ["src"],
|
|
29
28
|
"exclude": ["node_modules", "dist"]
|
|
30
29
|
}
|
package/next.config.mjs
DELETED
package/source.config.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { defineDocs, defineConfig, frontmatterSchema } from 'fumadocs-mdx/config'
|
|
2
|
-
import { z } from 'zod'
|
|
3
|
-
import remarkDirective from 'remark-directive'
|
|
4
|
-
import { remarkDirectiveAdmonition, remarkMdxMermaid } from 'fumadocs-core/mdx-plugins'
|
|
5
|
-
import remarkUnusedDirectives from './src/lib/remark-unused-directives'
|
|
6
|
-
|
|
7
|
-
const contentDir = process.env.CHRONICLE_CONTENT_DIR || './content'
|
|
8
|
-
|
|
9
|
-
export const docs = defineDocs({
|
|
10
|
-
dir: contentDir,
|
|
11
|
-
docs: {
|
|
12
|
-
schema: frontmatterSchema.extend({
|
|
13
|
-
order: z.number().optional(),
|
|
14
|
-
lastModified: z.string().optional(),
|
|
15
|
-
}),
|
|
16
|
-
postprocess: {
|
|
17
|
-
includeProcessedMarkdown: true,
|
|
18
|
-
},
|
|
19
|
-
files: ['**/*.mdx', '**/*.md', '!**/node_modules/**'],
|
|
20
|
-
},
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
export default defineConfig({
|
|
24
|
-
mdxOptions: {
|
|
25
|
-
remarkPlugins: [
|
|
26
|
-
remarkDirective,
|
|
27
|
-
[
|
|
28
|
-
remarkDirectiveAdmonition,
|
|
29
|
-
{
|
|
30
|
-
tags: {
|
|
31
|
-
CalloutContainer: 'Callout',
|
|
32
|
-
CalloutTitle: 'CalloutTitle',
|
|
33
|
-
CalloutDescription: 'CalloutDescription',
|
|
34
|
-
},
|
|
35
|
-
types: {
|
|
36
|
-
note: 'accent',
|
|
37
|
-
tip: 'accent',
|
|
38
|
-
info: 'accent',
|
|
39
|
-
warn: 'attention',
|
|
40
|
-
warning: 'attention',
|
|
41
|
-
danger: 'alert',
|
|
42
|
-
caution: 'alert',
|
|
43
|
-
success: 'success',
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
],
|
|
47
|
-
remarkUnusedDirectives,
|
|
48
|
-
remarkMdxMermaid,
|
|
49
|
-
],
|
|
50
|
-
},
|
|
51
|
-
})
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { loadConfig } from '@/lib/config'
|
|
2
|
-
import { buildPageTree } from '@/lib/source'
|
|
3
|
-
import { getTheme } from '@/themes/registry'
|
|
4
|
-
|
|
5
|
-
export default function DocsLayout({ children }: { children: React.ReactNode }) {
|
|
6
|
-
const config = loadConfig()
|
|
7
|
-
const tree = buildPageTree()
|
|
8
|
-
const { Layout, className } = getTheme(config.theme?.name)
|
|
9
|
-
|
|
10
|
-
return (
|
|
11
|
-
<Layout config={config} tree={tree} classNames={{ layout: className }}>
|
|
12
|
-
{children}
|
|
13
|
-
</Layout>
|
|
14
|
-
)
|
|
15
|
-
}
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import type { Metadata, ResolvingMetadata } from 'next'
|
|
2
|
-
import { notFound } from 'next/navigation'
|
|
3
|
-
import type { MDXContent } from 'mdx/types'
|
|
4
|
-
import { loadConfig } from '@/lib/config'
|
|
5
|
-
import { source, buildPageTree } from '@/lib/source'
|
|
6
|
-
import { getTheme } from '@/themes/registry'
|
|
7
|
-
import { mdxComponents } from '@/components/mdx'
|
|
8
|
-
|
|
9
|
-
interface PageProps {
|
|
10
|
-
params: Promise<{ slug?: string[] }>
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
interface PageData {
|
|
14
|
-
title: string
|
|
15
|
-
description?: string
|
|
16
|
-
body: MDXContent
|
|
17
|
-
toc: { title: string; url: string; depth: number }[]
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function generateMetadata(
|
|
21
|
-
{ params }: PageProps,
|
|
22
|
-
parent: ResolvingMetadata,
|
|
23
|
-
): Promise<Metadata> {
|
|
24
|
-
const { slug } = await params
|
|
25
|
-
const page = source.getPage(slug)
|
|
26
|
-
if (!page) return {}
|
|
27
|
-
const config = loadConfig()
|
|
28
|
-
const data = page.data as PageData
|
|
29
|
-
const parentMetadata = await parent
|
|
30
|
-
|
|
31
|
-
const metadata: Metadata = {
|
|
32
|
-
title: data.title,
|
|
33
|
-
description: data.description,
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (config.url) {
|
|
37
|
-
const ogParams = new URLSearchParams({ title: data.title })
|
|
38
|
-
if (data.description) ogParams.set('description', data.description)
|
|
39
|
-
metadata.openGraph = {
|
|
40
|
-
...parentMetadata.openGraph,
|
|
41
|
-
title: data.title,
|
|
42
|
-
description: data.description,
|
|
43
|
-
images: [{ url: `/og?${ogParams.toString()}`, width: 1200, height: 630 }],
|
|
44
|
-
}
|
|
45
|
-
metadata.twitter = {
|
|
46
|
-
...parentMetadata.twitter,
|
|
47
|
-
title: data.title,
|
|
48
|
-
description: data.description,
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return metadata
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export default async function DocsPage({ params }: PageProps) {
|
|
56
|
-
const { slug } = await params
|
|
57
|
-
const config = loadConfig()
|
|
58
|
-
|
|
59
|
-
const page = source.getPage(slug)
|
|
60
|
-
|
|
61
|
-
if (!page) {
|
|
62
|
-
notFound()
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const { Page } = getTheme(config.theme?.name)
|
|
66
|
-
|
|
67
|
-
const data = page.data as PageData
|
|
68
|
-
const MDXBody = data.body
|
|
69
|
-
|
|
70
|
-
const tree = buildPageTree()
|
|
71
|
-
|
|
72
|
-
const pageUrl = config.url ? `${config.url}/${(slug ?? []).join('/')}` : undefined
|
|
73
|
-
|
|
74
|
-
return (
|
|
75
|
-
<>
|
|
76
|
-
<script type="application/ld+json">
|
|
77
|
-
{JSON.stringify({
|
|
78
|
-
'@context': 'https://schema.org',
|
|
79
|
-
'@type': 'Article',
|
|
80
|
-
headline: data.title,
|
|
81
|
-
description: data.description,
|
|
82
|
-
...(pageUrl && { url: pageUrl }),
|
|
83
|
-
}, null, 2)}
|
|
84
|
-
</script>
|
|
85
|
-
<Page
|
|
86
|
-
page={{
|
|
87
|
-
slug: slug ?? [],
|
|
88
|
-
frontmatter: {
|
|
89
|
-
title: data.title,
|
|
90
|
-
description: data.description,
|
|
91
|
-
},
|
|
92
|
-
content: <MDXBody components={mdxComponents} />,
|
|
93
|
-
toc: data.toc ?? [],
|
|
94
|
-
}}
|
|
95
|
-
config={config}
|
|
96
|
-
tree={tree}
|
|
97
|
-
/>
|
|
98
|
-
</>
|
|
99
|
-
)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export function generateStaticParams() {
|
|
103
|
-
return source.getPages().map((page) => ({
|
|
104
|
-
slug: page.slugs,
|
|
105
|
-
}))
|
|
106
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
-
import { loadConfig } from "@/lib/config";
|
|
3
|
-
import { loadApiSpecs } from "@/lib/openapi";
|
|
4
|
-
|
|
5
|
-
export async function POST(request: NextRequest) {
|
|
6
|
-
const { specName, method, path, headers, body } = await request.json();
|
|
7
|
-
|
|
8
|
-
if (!specName || !method || !path) {
|
|
9
|
-
return NextResponse.json(
|
|
10
|
-
{ error: "Missing specName, method, or path" },
|
|
11
|
-
{ status: 400 },
|
|
12
|
-
);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const config = loadConfig();
|
|
16
|
-
const specs = loadApiSpecs(config.api ?? []);
|
|
17
|
-
const spec = specs.find((s) => s.name === specName);
|
|
18
|
-
|
|
19
|
-
if (!spec) {
|
|
20
|
-
return NextResponse.json(
|
|
21
|
-
{ error: `Unknown spec: ${specName}` },
|
|
22
|
-
{ status: 404 },
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const url = spec.server.url + path;
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
const response = await fetch(url, {
|
|
30
|
-
method,
|
|
31
|
-
headers,
|
|
32
|
-
body: body ? JSON.stringify(body) : undefined,
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const contentType = response.headers.get("content-type") ?? "";
|
|
36
|
-
const responseBody = contentType.includes("application/json")
|
|
37
|
-
? await response.json()
|
|
38
|
-
: await response.text();
|
|
39
|
-
|
|
40
|
-
return NextResponse.json({
|
|
41
|
-
status: response.status,
|
|
42
|
-
statusText: response.statusText,
|
|
43
|
-
body: responseBody,
|
|
44
|
-
}, { status: response.status });
|
|
45
|
-
} catch (error) {
|
|
46
|
-
const message =
|
|
47
|
-
error instanceof Error
|
|
48
|
-
? `${error.message}${error.cause ? `: ${(error.cause as Error).message}` : ""}`
|
|
49
|
-
: "Request failed";
|
|
50
|
-
return NextResponse.json(
|
|
51
|
-
{
|
|
52
|
-
status: 502,
|
|
53
|
-
statusText: "Bad Gateway",
|
|
54
|
-
body: `Could not reach ${url}\n${message}`,
|
|
55
|
-
},
|
|
56
|
-
{ status: 502 },
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { source } from '@/lib/source'
|
|
2
|
-
import { createSearchAPI, type AdvancedIndex } from 'fumadocs-core/search/server'
|
|
3
|
-
import type { StructuredData } from 'fumadocs-core/mdx-plugins'
|
|
4
|
-
import type { OpenAPIV3 } from 'openapi-types'
|
|
5
|
-
import { loadConfig } from '@/lib/config'
|
|
6
|
-
import { loadApiSpecs, type ApiSpec } from '@/lib/openapi'
|
|
7
|
-
import { getSpecSlug } from '@/lib/api-routes'
|
|
8
|
-
|
|
9
|
-
interface PageData {
|
|
10
|
-
title?: string
|
|
11
|
-
description?: string
|
|
12
|
-
structuredData?: StructuredData
|
|
13
|
-
load?: () => Promise<{ structuredData?: StructuredData }>
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const HTTP_METHODS = ['get', 'post', 'put', 'delete', 'patch'] as const
|
|
17
|
-
type HttpMethod = (typeof HTTP_METHODS)[number]
|
|
18
|
-
|
|
19
|
-
function getParamNames(op: OpenAPIV3.OperationObject): string[] {
|
|
20
|
-
const params = (op.parameters as OpenAPIV3.ParameterObject[] | undefined) ?? []
|
|
21
|
-
return params.map((p) => p.name)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function buildStructuredData(op: OpenAPIV3.OperationObject, method: string, pathStr: string) {
|
|
25
|
-
return {
|
|
26
|
-
headings: [{ id: op.operationId!, content: `${method.toUpperCase()} ${pathStr}` }],
|
|
27
|
-
contents: [{ heading: op.operationId!, content: `${method.toUpperCase()} ${[op.description, ...getParamNames(op)].filter(Boolean).join(' ')}` }],
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function operationToIndex(specSlug: string, pathStr: string, method: HttpMethod, op: OpenAPIV3.OperationObject): AdvancedIndex {
|
|
32
|
-
const url = `/apis/${specSlug}/${encodeURIComponent(op.operationId!)}`
|
|
33
|
-
return {
|
|
34
|
-
id: url,
|
|
35
|
-
url,
|
|
36
|
-
title: `${method.toUpperCase()} ${op.summary ?? op.operationId!}`,
|
|
37
|
-
description: op.description ?? '',
|
|
38
|
-
structuredData: buildStructuredData(op, method, pathStr),
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function pathEntryToIndexes(specSlug: string) {
|
|
43
|
-
return ([pathStr, pathItem]: [string, OpenAPIV3.PathItemObject | undefined]): AdvancedIndex[] => {
|
|
44
|
-
if (!pathItem) return []
|
|
45
|
-
const hasOp = (m: HttpMethod) => !!pathItem[m]?.operationId
|
|
46
|
-
const toIndex = (m: HttpMethod) => operationToIndex(specSlug, pathStr, m, pathItem[m]!)
|
|
47
|
-
return HTTP_METHODS.filter(hasOp).map(toIndex)
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function specToIndexes(spec: ApiSpec): AdvancedIndex[] {
|
|
52
|
-
const specSlug = getSpecSlug(spec)
|
|
53
|
-
return Object.entries(spec.document.paths ?? {}).flatMap(pathEntryToIndexes(specSlug))
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function buildApiIndexes(): AdvancedIndex[] {
|
|
57
|
-
const config = loadConfig()
|
|
58
|
-
if (!config.api?.length) return []
|
|
59
|
-
return loadApiSpecs(config.api).flatMap(specToIndexes)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export const { GET } = createSearchAPI('advanced', {
|
|
63
|
-
indexes: async (): Promise<AdvancedIndex[]> => {
|
|
64
|
-
const pages = source.getPages()
|
|
65
|
-
const indexes = await Promise.all(
|
|
66
|
-
pages.map(async (page): Promise<AdvancedIndex> => {
|
|
67
|
-
const data = page.data as PageData
|
|
68
|
-
let structuredData: StructuredData | undefined = data.structuredData
|
|
69
|
-
|
|
70
|
-
if (!structuredData && data.load) {
|
|
71
|
-
try {
|
|
72
|
-
const loaded = await data.load()
|
|
73
|
-
structuredData = loaded.structuredData
|
|
74
|
-
} catch (error) {
|
|
75
|
-
console.error(`Failed to load structured data for ${page.url}:`, error)
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
id: page.url,
|
|
81
|
-
url: page.url,
|
|
82
|
-
title: data.title ?? '',
|
|
83
|
-
description: data.description ?? '',
|
|
84
|
-
structuredData: structuredData ?? { headings: [], contents: [] },
|
|
85
|
-
}
|
|
86
|
-
})
|
|
87
|
-
)
|
|
88
|
-
return [...indexes, ...buildApiIndexes()]
|
|
89
|
-
},
|
|
90
|
-
})
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { cx } from 'class-variance-authority'
|
|
2
|
-
import { loadConfig } from '@/lib/config'
|
|
3
|
-
import { loadApiSpecs } from '@/lib/openapi'
|
|
4
|
-
import { buildApiPageTree } from '@/lib/api-routes'
|
|
5
|
-
import { getTheme } from '@/themes/registry'
|
|
6
|
-
import { Search } from '@/components/ui/search'
|
|
7
|
-
import styles from './layout.module.css'
|
|
8
|
-
|
|
9
|
-
export default function ApiLayout({ children }: { children: React.ReactNode }) {
|
|
10
|
-
const config = loadConfig()
|
|
11
|
-
const { Layout, className } = getTheme(config.theme?.name)
|
|
12
|
-
const specs = loadApiSpecs(config.api ?? [])
|
|
13
|
-
const tree = buildApiPageTree(specs)
|
|
14
|
-
|
|
15
|
-
return (
|
|
16
|
-
<Layout config={config} tree={tree} classNames={{
|
|
17
|
-
layout: cx(styles.layout, className),
|
|
18
|
-
body: styles.body,
|
|
19
|
-
sidebar: styles.sidebar,
|
|
20
|
-
content: styles.content,
|
|
21
|
-
}}>
|
|
22
|
-
<Search className={styles.hiddenSearch} />
|
|
23
|
-
{children}
|
|
24
|
-
</Layout>
|
|
25
|
-
)
|
|
26
|
-
}
|