@raystack/chronicle 0.1.0-canary.5a2be79
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/bin/chronicle.js +2 -0
- package/dist/cli/index.js +9980 -0
- package/next.config.mjs +10 -0
- package/package.json +63 -0
- package/source.config.ts +50 -0
- package/src/app/[[...slug]]/layout.tsx +15 -0
- package/src/app/[[...slug]]/page.tsx +57 -0
- package/src/app/api/apis-proxy/route.ts +59 -0
- package/src/app/api/health/route.ts +3 -0
- package/src/app/api/search/route.ts +90 -0
- package/src/app/apis/[[...slug]]/layout.module.css +22 -0
- package/src/app/apis/[[...slug]]/layout.tsx +26 -0
- package/src/app/apis/[[...slug]]/page.tsx +57 -0
- package/src/app/layout.tsx +26 -0
- package/src/app/llms-full.txt/route.ts +18 -0
- package/src/app/llms.txt/route.ts +15 -0
- package/src/app/providers.tsx +8 -0
- package/src/cli/commands/build.ts +32 -0
- package/src/cli/commands/dev.ts +33 -0
- package/src/cli/commands/init.ts +155 -0
- package/src/cli/commands/serve.ts +53 -0
- package/src/cli/commands/start.ts +33 -0
- package/src/cli/index.ts +21 -0
- package/src/cli/utils/config.ts +43 -0
- package/src/cli/utils/index.ts +3 -0
- package/src/cli/utils/process.ts +7 -0
- package/src/cli/utils/resolve.ts +4 -0
- package/src/cli/utils/scaffold.ts +131 -0
- package/src/components/api/code-snippets.module.css +7 -0
- package/src/components/api/code-snippets.tsx +76 -0
- package/src/components/api/endpoint-page.module.css +58 -0
- package/src/components/api/endpoint-page.tsx +283 -0
- package/src/components/api/field-row.module.css +126 -0
- package/src/components/api/field-row.tsx +204 -0
- package/src/components/api/field-section.module.css +24 -0
- package/src/components/api/field-section.tsx +100 -0
- package/src/components/api/index.ts +8 -0
- package/src/components/api/json-editor.module.css +9 -0
- package/src/components/api/json-editor.tsx +61 -0
- package/src/components/api/key-value-editor.module.css +13 -0
- package/src/components/api/key-value-editor.tsx +62 -0
- package/src/components/api/method-badge.module.css +4 -0
- package/src/components/api/method-badge.tsx +29 -0
- package/src/components/api/response-panel.module.css +8 -0
- package/src/components/api/response-panel.tsx +44 -0
- package/src/components/common/breadcrumb.tsx +3 -0
- package/src/components/common/button.tsx +3 -0
- package/src/components/common/callout.module.css +7 -0
- package/src/components/common/callout.tsx +27 -0
- package/src/components/common/code-block.tsx +3 -0
- package/src/components/common/dialog.tsx +3 -0
- package/src/components/common/index.ts +10 -0
- package/src/components/common/input-field.tsx +3 -0
- package/src/components/common/sidebar.tsx +3 -0
- package/src/components/common/switch.tsx +3 -0
- package/src/components/common/table.tsx +3 -0
- package/src/components/common/tabs.tsx +3 -0
- package/src/components/mdx/code.module.css +42 -0
- package/src/components/mdx/code.tsx +27 -0
- package/src/components/mdx/details.module.css +37 -0
- package/src/components/mdx/details.tsx +18 -0
- package/src/components/mdx/image.tsx +38 -0
- package/src/components/mdx/index.tsx +35 -0
- package/src/components/mdx/link.tsx +38 -0
- package/src/components/mdx/mermaid.module.css +9 -0
- package/src/components/mdx/mermaid.tsx +37 -0
- package/src/components/mdx/paragraph.module.css +8 -0
- package/src/components/mdx/paragraph.tsx +19 -0
- package/src/components/mdx/table.tsx +40 -0
- package/src/components/ui/breadcrumbs.tsx +72 -0
- package/src/components/ui/client-theme-switcher.tsx +18 -0
- package/src/components/ui/footer.module.css +27 -0
- package/src/components/ui/footer.tsx +30 -0
- package/src/components/ui/search.module.css +104 -0
- package/src/components/ui/search.tsx +202 -0
- package/src/lib/api-routes.ts +120 -0
- package/src/lib/config.ts +55 -0
- package/src/lib/get-llm-text.ts +10 -0
- package/src/lib/index.ts +2 -0
- package/src/lib/openapi.ts +188 -0
- package/src/lib/remark-unused-directives.ts +30 -0
- package/src/lib/schema.ts +99 -0
- package/src/lib/snippet-generators.ts +87 -0
- package/src/lib/source.ts +67 -0
- package/src/themes/default/Layout.module.css +81 -0
- package/src/themes/default/Layout.tsx +133 -0
- package/src/themes/default/Page.module.css +46 -0
- package/src/themes/default/Page.tsx +21 -0
- package/src/themes/default/Toc.module.css +48 -0
- package/src/themes/default/Toc.tsx +66 -0
- package/src/themes/default/font.ts +6 -0
- package/src/themes/default/index.ts +13 -0
- package/src/themes/paper/ChapterNav.module.css +71 -0
- package/src/themes/paper/ChapterNav.tsx +96 -0
- package/src/themes/paper/Layout.module.css +33 -0
- package/src/themes/paper/Layout.tsx +25 -0
- package/src/themes/paper/Page.module.css +174 -0
- package/src/themes/paper/Page.tsx +107 -0
- package/src/themes/paper/ReadingProgress.module.css +132 -0
- package/src/themes/paper/ReadingProgress.tsx +294 -0
- package/src/themes/paper/index.ts +8 -0
- package/src/themes/registry.ts +14 -0
- package/src/types/config.ts +69 -0
- package/src/types/content.ts +35 -0
- package/src/types/index.ts +3 -0
- package/src/types/theme.ts +22 -0
- package/tsconfig.json +30 -0
package/next.config.mjs
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@raystack/chronicle",
|
|
3
|
+
"version": "0.1.0-canary.5a2be79",
|
|
4
|
+
"description": "Config-driven documentation framework",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"files": [
|
|
8
|
+
"bin",
|
|
9
|
+
"dist",
|
|
10
|
+
"src",
|
|
11
|
+
"templates",
|
|
12
|
+
"next.config.mjs",
|
|
13
|
+
"source.config.ts",
|
|
14
|
+
"tsconfig.json"
|
|
15
|
+
],
|
|
16
|
+
"bin": {
|
|
17
|
+
"chronicle": "./bin/chronicle.js"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build:cli": "bun build-cli.ts"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@biomejs/biome": "^2.3.13",
|
|
24
|
+
"@raystack/tools-config": "0.56.0",
|
|
25
|
+
"@types/lodash": "^4.17.23",
|
|
26
|
+
"@types/mdx": "^2.0.13",
|
|
27
|
+
"@types/node": "^25.1.0",
|
|
28
|
+
"@types/react": "^19.2.10",
|
|
29
|
+
"@types/react-dom": "^19.2.3",
|
|
30
|
+
"@types/semver": "^7.7.1",
|
|
31
|
+
"semver": "^7.7.4",
|
|
32
|
+
"typescript": "5.9.3"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@codemirror/lang-json": "^6.0.2",
|
|
36
|
+
"@codemirror/state": "^6.5.4",
|
|
37
|
+
"@codemirror/theme-one-dark": "^6.1.3",
|
|
38
|
+
"@codemirror/view": "^6.39.14",
|
|
39
|
+
"@heroicons/react": "^2.2.0",
|
|
40
|
+
"@raystack/apsara": "^0.56.0",
|
|
41
|
+
"@types/unist": "^3.0.3",
|
|
42
|
+
"chalk": "^5.6.2",
|
|
43
|
+
"class-variance-authority": "^0.7.1",
|
|
44
|
+
"codemirror": "^6.0.2",
|
|
45
|
+
"commander": "^14.0.2",
|
|
46
|
+
"fumadocs-core": "16.6.15",
|
|
47
|
+
"fumadocs-mdx": "^14.2.6",
|
|
48
|
+
"lodash": "^4.17.23",
|
|
49
|
+
"mermaid": "^11.13.0",
|
|
50
|
+
"next": "16.1.6",
|
|
51
|
+
"react": "^19.0.0",
|
|
52
|
+
"react-device-detect": "^2.2.3",
|
|
53
|
+
"react-dom": "^19.0.0",
|
|
54
|
+
"remark-attr": "^0.11.1",
|
|
55
|
+
"remark-directive": "^4.0.0",
|
|
56
|
+
"slugify": "^1.6.6",
|
|
57
|
+
"unified": "^11.0.5",
|
|
58
|
+
"unist-util-visit": "^5.1.0",
|
|
59
|
+
"openapi-types": "^12.1.3",
|
|
60
|
+
"yaml": "^2.8.2",
|
|
61
|
+
"zod": "^4.3.6"
|
|
62
|
+
}
|
|
63
|
+
}
|
package/source.config.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
}),
|
|
15
|
+
postprocess: {
|
|
16
|
+
includeProcessedMarkdown: true,
|
|
17
|
+
},
|
|
18
|
+
files: ['**/*.mdx', '**/*.md', '!**/node_modules/**'],
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
export default defineConfig({
|
|
23
|
+
mdxOptions: {
|
|
24
|
+
remarkPlugins: [
|
|
25
|
+
remarkDirective,
|
|
26
|
+
[
|
|
27
|
+
remarkDirectiveAdmonition,
|
|
28
|
+
{
|
|
29
|
+
tags: {
|
|
30
|
+
CalloutContainer: 'Callout',
|
|
31
|
+
CalloutTitle: 'CalloutTitle',
|
|
32
|
+
CalloutDescription: 'CalloutDescription',
|
|
33
|
+
},
|
|
34
|
+
types: {
|
|
35
|
+
note: 'accent',
|
|
36
|
+
tip: 'accent',
|
|
37
|
+
info: 'accent',
|
|
38
|
+
warn: 'attention',
|
|
39
|
+
warning: 'attention',
|
|
40
|
+
danger: 'alert',
|
|
41
|
+
caution: 'alert',
|
|
42
|
+
success: 'success',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
remarkUnusedDirectives,
|
|
47
|
+
remarkMdxMermaid,
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { notFound } from 'next/navigation'
|
|
2
|
+
import type { MDXContent } from 'mdx/types'
|
|
3
|
+
import { loadConfig } from '@/lib/config'
|
|
4
|
+
import { source, buildPageTree } from '@/lib/source'
|
|
5
|
+
import { getTheme } from '@/themes/registry'
|
|
6
|
+
import { mdxComponents } from '@/components/mdx'
|
|
7
|
+
|
|
8
|
+
interface PageProps {
|
|
9
|
+
params: Promise<{ slug?: string[] }>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface PageData {
|
|
13
|
+
title: string
|
|
14
|
+
description?: string
|
|
15
|
+
body: MDXContent
|
|
16
|
+
toc: { title: string; url: string; depth: number }[]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default async function DocsPage({ params }: PageProps) {
|
|
20
|
+
const { slug } = await params
|
|
21
|
+
const config = loadConfig()
|
|
22
|
+
|
|
23
|
+
const page = source.getPage(slug)
|
|
24
|
+
|
|
25
|
+
if (!page) {
|
|
26
|
+
notFound()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const { Page } = getTheme(config.theme?.name)
|
|
30
|
+
|
|
31
|
+
const data = page.data as PageData
|
|
32
|
+
const MDXBody = data.body
|
|
33
|
+
|
|
34
|
+
const tree = buildPageTree()
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<Page
|
|
38
|
+
page={{
|
|
39
|
+
slug: slug ?? [],
|
|
40
|
+
frontmatter: {
|
|
41
|
+
title: data.title,
|
|
42
|
+
description: data.description,
|
|
43
|
+
},
|
|
44
|
+
content: <MDXBody components={mdxComponents} />,
|
|
45
|
+
toc: data.toc ?? [],
|
|
46
|
+
}}
|
|
47
|
+
config={config}
|
|
48
|
+
tree={tree}
|
|
49
|
+
/>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function generateStaticParams() {
|
|
54
|
+
return source.getPages().map((page) => ({
|
|
55
|
+
slug: page.slugs,
|
|
56
|
+
}))
|
|
57
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
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
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
.layout {
|
|
2
|
+
height: 100vh;
|
|
3
|
+
overflow: hidden;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.body {
|
|
7
|
+
overflow: hidden;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.sidebar {
|
|
11
|
+
height: 100%;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.content {
|
|
15
|
+
height: 100%;
|
|
16
|
+
overflow-y: auto;
|
|
17
|
+
padding-right: 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.hiddenSearch {
|
|
21
|
+
display: none;
|
|
22
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { notFound } from 'next/navigation'
|
|
2
|
+
import type { OpenAPIV3 } from 'openapi-types'
|
|
3
|
+
import { Flex, Headline, Text } from '@raystack/apsara'
|
|
4
|
+
import { loadConfig } from '@/lib/config'
|
|
5
|
+
import { loadApiSpecs } from '@/lib/openapi'
|
|
6
|
+
import { buildApiRoutes, findApiOperation } from '@/lib/api-routes'
|
|
7
|
+
import { EndpointPage } from '@/components/api'
|
|
8
|
+
|
|
9
|
+
interface PageProps {
|
|
10
|
+
params: Promise<{ slug?: string[] }>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default async function ApiPage({ params }: PageProps) {
|
|
14
|
+
const { slug } = await params
|
|
15
|
+
const config = loadConfig()
|
|
16
|
+
const specs = loadApiSpecs(config.api ?? [])
|
|
17
|
+
|
|
18
|
+
if (!slug || slug.length === 0) {
|
|
19
|
+
return <ApiLanding specs={specs} />
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const match = findApiOperation(specs, slug)
|
|
23
|
+
if (!match) notFound()
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<EndpointPage
|
|
27
|
+
method={match.method}
|
|
28
|
+
path={match.path}
|
|
29
|
+
operation={match.operation}
|
|
30
|
+
serverUrl={match.spec.server.url}
|
|
31
|
+
specName={match.spec.name}
|
|
32
|
+
auth={match.spec.auth}
|
|
33
|
+
/>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function ApiLanding({ specs }: { specs: { name: string; document: OpenAPIV3.Document }[] }) {
|
|
38
|
+
return (
|
|
39
|
+
<Flex direction="column" gap="large" style={{ padding: 'var(--rs-space-7)' }}>
|
|
40
|
+
<Headline size="medium" as="h1">API Reference</Headline>
|
|
41
|
+
{specs.map((spec) => (
|
|
42
|
+
<Flex key={spec.name} direction="column" gap="small">
|
|
43
|
+
<Headline size="small" as="h2">{spec.name}</Headline>
|
|
44
|
+
{spec.document.info.description && (
|
|
45
|
+
<Text size={3}>{spec.document.info.description}</Text>
|
|
46
|
+
)}
|
|
47
|
+
</Flex>
|
|
48
|
+
))}
|
|
49
|
+
</Flex>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function generateStaticParams() {
|
|
54
|
+
const config = loadConfig()
|
|
55
|
+
const specs = loadApiSpecs(config.api ?? [])
|
|
56
|
+
return [{ slug: [] }, ...buildApiRoutes(specs)]
|
|
57
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import '@raystack/apsara/normalize.css'
|
|
2
|
+
import '@raystack/apsara/style.css'
|
|
3
|
+
import type { Metadata } from 'next'
|
|
4
|
+
import { loadConfig } from '@/lib/config'
|
|
5
|
+
import { Providers } from './providers'
|
|
6
|
+
|
|
7
|
+
const config = loadConfig()
|
|
8
|
+
|
|
9
|
+
export const metadata: Metadata = {
|
|
10
|
+
title: config.title,
|
|
11
|
+
description: config.description,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default function RootLayout({
|
|
15
|
+
children,
|
|
16
|
+
}: {
|
|
17
|
+
children: React.ReactNode
|
|
18
|
+
}) {
|
|
19
|
+
return (
|
|
20
|
+
<html lang="en" suppressHydrationWarning>
|
|
21
|
+
<body suppressHydrationWarning>
|
|
22
|
+
<Providers>{children}</Providers>
|
|
23
|
+
</body>
|
|
24
|
+
</html>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { source } from '@/lib/source'
|
|
2
|
+
import { loadConfig } from '@/lib/config'
|
|
3
|
+
import { getLLMText } from '@/lib/get-llm-text'
|
|
4
|
+
|
|
5
|
+
export const revalidate = false
|
|
6
|
+
|
|
7
|
+
export async function GET() {
|
|
8
|
+
const config = loadConfig()
|
|
9
|
+
|
|
10
|
+
if (!config.llms?.enabled) {
|
|
11
|
+
return new Response('Not Found', { status: 404 })
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const scan = source.getPages().map(getLLMText)
|
|
15
|
+
const scanned = await Promise.all(scan)
|
|
16
|
+
|
|
17
|
+
return new Response(scanned.join('\n\n'))
|
|
18
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { source } from '@/lib/source'
|
|
2
|
+
import { loadConfig } from '@/lib/config'
|
|
3
|
+
import { llms } from 'fumadocs-core/source'
|
|
4
|
+
|
|
5
|
+
export const revalidate = false
|
|
6
|
+
|
|
7
|
+
export function GET() {
|
|
8
|
+
const config = loadConfig()
|
|
9
|
+
|
|
10
|
+
if (!config.llms?.enabled) {
|
|
11
|
+
return new Response('Not Found', { status: 404 })
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return new Response(llms(source).index())
|
|
15
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { spawn } from 'child_process'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import fs from 'fs'
|
|
5
|
+
import chalk from 'chalk'
|
|
6
|
+
import { attachLifecycleHandlers, resolveNextCli } from '@/cli/utils'
|
|
7
|
+
|
|
8
|
+
export const buildCommand = new Command('build')
|
|
9
|
+
.description('Build for production')
|
|
10
|
+
.action(() => {
|
|
11
|
+
const scaffoldPath = path.join(process.cwd(), '.chronicle')
|
|
12
|
+
if (!fs.existsSync(scaffoldPath)) {
|
|
13
|
+
console.log(chalk.red('Error: .chronicle/ not found. Run'), chalk.cyan('chronicle init'), chalk.red('first.'))
|
|
14
|
+
process.exit(1)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const nextCli = resolveNextCli()
|
|
18
|
+
|
|
19
|
+
console.log(chalk.cyan('Building for production...'))
|
|
20
|
+
|
|
21
|
+
const child = spawn(process.execPath, [nextCli, 'build'], {
|
|
22
|
+
stdio: 'inherit',
|
|
23
|
+
cwd: scaffoldPath,
|
|
24
|
+
env: {
|
|
25
|
+
...process.env,
|
|
26
|
+
CHRONICLE_PROJECT_ROOT: process.cwd(),
|
|
27
|
+
CHRONICLE_CONTENT_DIR: './content',
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
attachLifecycleHandlers(child)
|
|
32
|
+
})
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { spawn } from 'child_process'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import fs from 'fs'
|
|
5
|
+
import chalk from 'chalk'
|
|
6
|
+
import { attachLifecycleHandlers, resolveNextCli } from '@/cli/utils'
|
|
7
|
+
|
|
8
|
+
export const devCommand = new Command('dev')
|
|
9
|
+
.description('Start development server')
|
|
10
|
+
.option('-p, --port <port>', 'Port number', '3000')
|
|
11
|
+
.action((options) => {
|
|
12
|
+
const scaffoldPath = path.join(process.cwd(), '.chronicle')
|
|
13
|
+
if (!fs.existsSync(scaffoldPath)) {
|
|
14
|
+
console.log(chalk.red('Error: .chronicle/ not found. Run'), chalk.cyan('chronicle init'), chalk.red('first.'))
|
|
15
|
+
process.exit(1)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const nextCli = resolveNextCli()
|
|
19
|
+
|
|
20
|
+
console.log(chalk.cyan('Starting dev server...'))
|
|
21
|
+
|
|
22
|
+
const child = spawn(process.execPath, [nextCli, 'dev', '-p', options.port], {
|
|
23
|
+
stdio: 'inherit',
|
|
24
|
+
cwd: scaffoldPath,
|
|
25
|
+
env: {
|
|
26
|
+
...process.env,
|
|
27
|
+
CHRONICLE_PROJECT_ROOT: process.cwd(),
|
|
28
|
+
CHRONICLE_CONTENT_DIR: './content',
|
|
29
|
+
},
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
attachLifecycleHandlers(child)
|
|
33
|
+
})
|