@raystack/chronicle 0.1.0-canary.5a730d4 → 0.1.0-canary.67113f8

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.
Files changed (85) hide show
  1. package/dist/cli/index.js +217 -412
  2. package/package.json +13 -10
  3. package/src/cli/commands/build.ts +30 -48
  4. package/src/cli/commands/dev.ts +24 -13
  5. package/src/cli/commands/init.ts +38 -123
  6. package/src/cli/commands/serve.ts +35 -50
  7. package/src/cli/commands/start.ts +20 -16
  8. package/src/cli/index.ts +14 -14
  9. package/src/cli/utils/config.ts +25 -26
  10. package/src/cli/utils/index.ts +3 -2
  11. package/src/cli/utils/resolve.ts +7 -3
  12. package/src/cli/utils/scaffold.ts +14 -16
  13. package/src/components/mdx/details.module.css +0 -2
  14. package/src/components/mdx/image.tsx +5 -20
  15. package/src/components/mdx/index.tsx +18 -4
  16. package/src/components/mdx/link.tsx +24 -20
  17. package/src/components/ui/breadcrumbs.tsx +8 -42
  18. package/src/components/ui/footer.tsx +2 -3
  19. package/src/components/ui/search.tsx +116 -72
  20. package/src/lib/api-routes.ts +6 -8
  21. package/src/lib/config.ts +31 -29
  22. package/src/lib/get-llm-text.ts +10 -0
  23. package/src/lib/head.tsx +26 -22
  24. package/src/lib/mdx-loader.ts +21 -0
  25. package/src/lib/openapi.ts +8 -8
  26. package/src/lib/page-context.tsx +74 -58
  27. package/src/lib/source.ts +136 -114
  28. package/src/pages/ApiLayout.tsx +22 -18
  29. package/src/pages/ApiPage.tsx +32 -27
  30. package/src/pages/DocsLayout.tsx +7 -7
  31. package/src/pages/DocsPage.tsx +11 -11
  32. package/src/pages/NotFound.tsx +11 -4
  33. package/src/server/App.tsx +35 -27
  34. package/src/server/api/apis-proxy.ts +69 -0
  35. package/src/server/api/health.ts +5 -0
  36. package/src/server/api/page/[...slug].ts +17 -0
  37. package/src/server/api/search.ts +170 -0
  38. package/src/server/api/specs.ts +9 -0
  39. package/src/server/build-search-index.ts +117 -0
  40. package/src/server/entry-client.tsx +47 -55
  41. package/src/server/entry-server.tsx +100 -35
  42. package/src/server/routes/llms.txt.ts +61 -0
  43. package/src/server/routes/og.tsx +75 -0
  44. package/src/server/routes/robots.txt.ts +11 -0
  45. package/src/server/routes/sitemap.xml.ts +40 -0
  46. package/src/server/utils/safe-path.ts +17 -0
  47. package/src/server/vite-config.ts +90 -44
  48. package/src/themes/default/Layout.tsx +78 -47
  49. package/src/themes/default/Page.module.css +0 -12
  50. package/src/themes/default/Page.tsx +9 -11
  51. package/src/themes/default/Toc.tsx +25 -39
  52. package/src/themes/default/index.ts +7 -9
  53. package/src/themes/paper/ChapterNav.tsx +63 -43
  54. package/src/themes/paper/Layout.module.css +1 -1
  55. package/src/themes/paper/Layout.tsx +24 -12
  56. package/src/themes/paper/Page.module.css +16 -4
  57. package/src/themes/paper/Page.tsx +56 -62
  58. package/src/themes/paper/ReadingProgress.tsx +160 -139
  59. package/src/themes/paper/index.ts +5 -5
  60. package/src/themes/registry.ts +7 -7
  61. package/src/types/content.ts +5 -21
  62. package/src/types/globals.d.ts +3 -0
  63. package/src/types/theme.ts +4 -3
  64. package/src/cli/__tests__/config.test.ts +0 -25
  65. package/src/cli/__tests__/scaffold.test.ts +0 -10
  66. package/src/pages/__tests__/head.test.tsx +0 -57
  67. package/src/server/__tests__/entry-server.test.tsx +0 -35
  68. package/src/server/__tests__/handlers.test.ts +0 -77
  69. package/src/server/__tests__/og.test.ts +0 -23
  70. package/src/server/__tests__/router.test.ts +0 -72
  71. package/src/server/__tests__/vite-config.test.ts +0 -25
  72. package/src/server/dev.ts +0 -156
  73. package/src/server/entry-prod.ts +0 -127
  74. package/src/server/handlers/apis-proxy.ts +0 -52
  75. package/src/server/handlers/health.ts +0 -3
  76. package/src/server/handlers/llms.ts +0 -58
  77. package/src/server/handlers/og.ts +0 -87
  78. package/src/server/handlers/robots.ts +0 -11
  79. package/src/server/handlers/search.ts +0 -140
  80. package/src/server/handlers/sitemap.ts +0 -39
  81. package/src/server/handlers/specs.ts +0 -9
  82. package/src/server/index.html +0 -12
  83. package/src/server/prod.ts +0 -18
  84. package/src/server/router.ts +0 -42
  85. package/src/themes/default/font.ts +0 -4
package/src/lib/source.ts CHANGED
@@ -1,138 +1,160 @@
1
- import type { MDXContent } from 'mdx/types'
2
- import type { Frontmatter, PageTree, PageTreeItem } from '@/types'
3
-
4
- const meta: Record<string, Frontmatter> = import.meta.glob(
5
- '@content/**/*.{mdx,md}',
6
- { eager: true, import: 'frontmatter' }
7
- )
8
-
9
- const loaders: Record<string, () => Promise<{ default: MDXContent }>> = import.meta.glob(
10
- '@content/**/*.{mdx,md}'
11
- )
12
-
13
- export interface SourcePage {
14
- url: string
15
- slugs: string[]
16
- filePath: string
17
- frontmatter: Frontmatter
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { loader } from 'fumadocs-core/source';
4
+ import type { Root, Node, Folder } from 'fumadocs-core/page-tree';
5
+ import matter from 'gray-matter';
6
+ import type { MDXContent } from 'mdx/types';
7
+ import type { TableOfContents } from 'fumadocs-core/toc';
8
+ import type { Frontmatter } from '@/types';
9
+
10
+ function getContentDir(): string {
11
+ return __CHRONICLE_CONTENT_DIR__ || path.join(process.cwd(), 'content');
18
12
  }
19
13
 
20
- // Compute common directory prefix of all glob keys once
21
- function computePrefix(keys: string[]): string {
22
- if (keys.length === 0) return ''
23
- const dirs = keys.map((k) => k.split('/').slice(0, -1)) // drop filename
24
- const first = dirs[0]
25
- let depth = 0
26
- for (let i = 0; i < first.length; i++) {
27
- if (dirs.every((d) => d[i] === first[i])) {
28
- depth = i + 1
29
- } else {
30
- break
14
+ async function scanFiles(contentDir: string) {
15
+ const files: {
16
+ type: 'page' | 'meta';
17
+ path: string;
18
+ data: Record<string, unknown>;
19
+ }[] = [];
20
+
21
+ async function scan(dir: string, prefix: string[] = []) {
22
+ try {
23
+ const entries = await fs.readdir(dir, { withFileTypes: true });
24
+ for (const entry of entries) {
25
+ if (entry.name.startsWith('.') || entry.name === 'node_modules')
26
+ continue;
27
+ const fullPath = path.join(dir, entry.name);
28
+ const relativePath = [...prefix, entry.name].join('/');
29
+
30
+ if (entry.isDirectory()) {
31
+ await scan(fullPath, [...prefix, entry.name]);
32
+ continue;
33
+ }
34
+
35
+ if (entry.name.endsWith('.mdx') || entry.name.endsWith('.md')) {
36
+ const raw = await fs.readFile(fullPath, 'utf-8');
37
+ const { data } = matter(raw);
38
+ files.push({
39
+ type: 'page',
40
+ path: relativePath,
41
+ data: { ...data, _relativePath: relativePath }
42
+ });
43
+ } else if (entry.name === 'meta.json' || entry.name === 'meta.yaml') {
44
+ try {
45
+ const raw = await fs.readFile(fullPath, 'utf-8');
46
+ const data = entry.name.endsWith('.json')
47
+ ? JSON.parse(raw)
48
+ : matter(raw).data;
49
+ files.push({ type: 'meta', path: relativePath, data });
50
+ } catch {
51
+ /* malformed meta file */
52
+ }
53
+ }
54
+ }
55
+ } catch {
56
+ /* directory not readable */
31
57
  }
32
58
  }
33
- return first.slice(0, depth).join('/') + '/'
34
- }
35
59
 
36
- const prefix = computePrefix(Object.keys(meta))
37
-
38
- function filePathToSlugs(filePath: string): string[] {
39
- const relative = filePath.slice(prefix.length)
40
- const withoutExt = relative.replace(/\.(mdx|md)$/, '')
41
- const parts = withoutExt.split('/').filter(Boolean)
42
- if (parts[parts.length - 1] === 'index') parts.pop()
43
- return parts
60
+ await scan(contentDir);
61
+ return files;
44
62
  }
45
63
 
46
- function slugsToUrl(slugs: string[]): string {
47
- return slugs.length === 0 ? '/' : '/' + slugs.join('/')
64
+ let cachedSource: ReturnType<typeof loader> | null = null;
65
+
66
+ async function getSource() {
67
+ if (cachedSource) return cachedSource;
68
+ const contentDir = getContentDir();
69
+ const files = await scanFiles(contentDir);
70
+ cachedSource = loader({
71
+ source: { files },
72
+ baseUrl: '/'
73
+ });
74
+ return cachedSource;
48
75
  }
49
76
 
50
- let cachedPages: SourcePage[] | null = null
51
-
52
- export async function getPages(): Promise<SourcePage[]> {
53
- if (cachedPages) return cachedPages
54
-
55
- cachedPages = Object.entries(meta).map(([filePath, fm]) => {
56
- const slugs = filePathToSlugs(filePath)
57
- const baseName = slugs[slugs.length - 1] ?? 'index'
58
- return {
59
- url: slugsToUrl(slugs),
60
- slugs,
61
- filePath,
62
- frontmatter: {
63
- title: fm?.title ?? baseName,
64
- description: fm?.description,
65
- order: fm?.order,
66
- icon: fm?.icon,
67
- lastModified: fm?.lastModified,
68
- },
69
- }
70
- })
77
+ export { getSource as source };
71
78
 
72
- return cachedPages
79
+ export function invalidate() {
80
+ cachedSource = null;
73
81
  }
74
82
 
75
- export async function getPage(slug?: string[]): Promise<SourcePage | null> {
76
- const pages = await getPages()
77
- const targetUrl = !slug || slug.length === 0 ? '/' : '/' + slug.join('/')
78
- return pages.find((p) => p.url === targetUrl) ?? null
83
+ function getOrder(node: Node, orderMap: Map<string, number>): number | undefined {
84
+ if (node.type === 'page') return orderMap.get(node.url);
85
+ if (node.type === 'folder' && node.index) return orderMap.get(node.index.url);
86
+ return undefined;
79
87
  }
80
88
 
81
- export async function loadPageComponent(page: SourcePage): Promise<MDXContent | null> {
82
- const loader = loaders[page.filePath]
83
- if (!loader) return null
84
- const mod = await loader()
85
- return mod.default
89
+ function sortNodes(nodes: Node[], orderMap: Map<string, number>): Node[] {
90
+ return [...nodes]
91
+ .map(n =>
92
+ n.type === 'folder'
93
+ ? ({ ...n, children: sortNodes(n.children, orderMap) } as Folder)
94
+ : n
95
+ )
96
+ .sort(
97
+ (a, b) =>
98
+ (getOrder(a, orderMap) ?? Number.MAX_SAFE_INTEGER) -
99
+ (getOrder(b, orderMap) ?? Number.MAX_SAFE_INTEGER)
100
+ );
86
101
  }
87
102
 
88
- export function invalidate() {
89
- cachedPages = null
103
+ function sortTreeByOrder(tree: Root, pages: { url: string; data: unknown }[]): Root {
104
+ const orderMap = new Map<string, number>();
105
+ for (const page of pages) {
106
+ const d = page.data as Record<string, unknown>;
107
+ const order = d.order as number | undefined;
108
+ if (order !== undefined) orderMap.set(page.url, order);
109
+ if (page.url === '/') orderMap.set('/', order ?? 0);
110
+ }
111
+ return { ...tree, children: sortNodes(tree.children, orderMap) };
90
112
  }
91
113
 
92
- export async function buildPageTree(): Promise<PageTree> {
93
- const pages = await getPages()
94
- const folders = new Map<string, PageTreeItem[]>()
95
- const rootPages: PageTreeItem[] = []
96
-
97
- pages.forEach((page) => {
98
- const isIndex = page.url === '/'
99
- const item: PageTreeItem = {
100
- type: 'page',
101
- name: page.frontmatter.title,
102
- url: page.url,
103
- order: page.frontmatter.order ?? (isIndex ? 0 : undefined),
104
- }
105
-
106
- if (page.slugs.length > 1) {
107
- const folder = page.slugs[0]
108
- if (!folders.has(folder)) {
109
- folders.set(folder, [])
110
- }
111
- folders.get(folder)?.push(item)
112
- } else {
113
- rootPages.push(item)
114
- }
115
- })
114
+ export async function getPageTree(): Promise<Root> {
115
+ const s = await getSource();
116
+ return sortTreeByOrder(s.pageTree as Root, s.getPages());
117
+ }
116
118
 
117
- const sortByOrder = (items: PageTreeItem[]) =>
118
- items.sort((a, b) => (a.order ?? Number.MAX_SAFE_INTEGER) - (b.order ?? Number.MAX_SAFE_INTEGER))
119
+ export async function getPages() {
120
+ const s = await getSource();
121
+ return s.getPages();
122
+ }
119
123
 
120
- const children: PageTreeItem[] = sortByOrder(rootPages)
124
+ export async function getPage(slugs?: string[]) {
125
+ const s = await getSource();
126
+ return s.getPage(slugs);
127
+ }
121
128
 
122
- const folderItems: PageTreeItem[] = []
123
- folders.forEach((items, folder) => {
124
- const sorted = sortByOrder(items)
125
- const indexPage = items.find(item => item.url === `/${folder}`)
126
- const folderOrder = indexPage?.order ?? sorted[0]?.order
127
- folderItems.push({
128
- type: 'folder',
129
- name: folder.charAt(0).toUpperCase() + folder.slice(1),
130
- order: folderOrder,
131
- children: sorted,
132
- })
133
- })
129
+ export function extractFrontmatter(page: { data: unknown }, fallbackTitle?: string): Frontmatter {
130
+ const d = page.data as Record<string, unknown>;
131
+ return {
132
+ title: (d.title as string) ?? fallbackTitle ?? 'Untitled',
133
+ description: d.description as string | undefined,
134
+ order: d.order as number | undefined,
135
+ icon: d.icon as string | undefined,
136
+ lastModified: d.lastModified as string | undefined,
137
+ };
138
+ }
134
139
 
135
- children.push(...sortByOrder(folderItems))
140
+ export function getRelativePath(page: { data: unknown }): string {
141
+ return ((page.data as Record<string, unknown>)._relativePath as string) ?? '';
142
+ }
136
143
 
137
- return { name: 'root', children }
144
+ const ssrModules = import.meta.glob<{ default?: MDXContent; toc?: TableOfContents }>(
145
+ '../../.content/**/*.{mdx,md}'
146
+ );
147
+
148
+ export async function loadPageModule(
149
+ relativePath: string
150
+ ): Promise<{ default: MDXContent | null; toc: TableOfContents }> {
151
+ if (!relativePath || relativePath.includes('..')) return { default: null, toc: [] };
152
+ const withoutExt = relativePath.replace(/\.(mdx|md)$/, '');
153
+ const key = relativePath.endsWith('.md')
154
+ ? `../../.content/${withoutExt}.md`
155
+ : `../../.content/${withoutExt}.mdx`;
156
+ const loader = ssrModules[key];
157
+ if (!loader) return { default: null, toc: [] };
158
+ const mod = await loader();
159
+ return { default: mod.default ?? null, toc: mod.toc ?? [] };
138
160
  }
@@ -1,29 +1,33 @@
1
- import type { ReactNode } from 'react'
2
- import { cx } from 'class-variance-authority'
3
- import { usePageContext } from '@/lib/page-context'
4
- import { buildApiPageTree } from '@/lib/api-routes'
5
- import { getTheme } from '@/themes/registry'
6
- import { Search } from '@/components/ui/search'
7
- import styles from './ApiLayout.module.css'
1
+ import { cx } from 'class-variance-authority';
2
+ import type { ReactNode } from 'react';
3
+ import { Search } from '@/components/ui/search';
4
+ import { buildApiPageTree } from '@/lib/api-routes';
5
+ import { usePageContext } from '@/lib/page-context';
6
+ import { getTheme } from '@/themes/registry';
7
+ import styles from './ApiLayout.module.css';
8
8
 
9
9
  interface ApiLayoutProps {
10
- children: ReactNode
10
+ children: ReactNode;
11
11
  }
12
12
 
13
13
  export function ApiLayout({ children }: ApiLayoutProps) {
14
- const { config, apiSpecs } = usePageContext()
15
- const { Layout, className } = getTheme(config.theme?.name)
16
- const tree = buildApiPageTree(apiSpecs)
14
+ const { config, apiSpecs } = usePageContext();
15
+ const { Layout, className } = getTheme(config.theme?.name);
16
+ const tree = buildApiPageTree(apiSpecs);
17
17
 
18
18
  return (
19
- <Layout config={config} tree={tree} classNames={{
20
- layout: cx(styles.layout, className),
21
- body: styles.body,
22
- sidebar: styles.sidebar,
23
- content: styles.content,
24
- }}>
19
+ <Layout
20
+ config={config}
21
+ tree={tree}
22
+ classNames={{
23
+ layout: cx(styles.layout, className),
24
+ body: styles.body,
25
+ sidebar: styles.sidebar,
26
+ content: styles.content
27
+ }}
28
+ >
25
29
  <Search className={styles.hiddenSearch} />
26
30
  {children}
27
31
  </Layout>
28
- )
32
+ );
29
33
  }
@@ -1,44 +1,41 @@
1
- import type { OpenAPIV3 } from 'openapi-types'
2
- import { Flex, Headline, Text } from '@raystack/apsara'
3
- import { usePageContext } from '@/lib/page-context'
4
- import { findApiOperation } from '@/lib/api-routes'
5
- import { EndpointPage } from '@/components/api'
6
- import { Head } from '@/lib/head'
7
- import type { ApiSpec } from '@/lib/openapi'
1
+ import { Flex, Headline, Text } from '@raystack/apsara';
2
+ import type { OpenAPIV3 } from 'openapi-types';
3
+ import { EndpointPage } from '@/components/api';
4
+ import { findApiOperation } from '@/lib/api-routes';
5
+ import { Head } from '@/lib/head';
6
+ import type { ApiSpec } from '@/lib/openapi';
7
+ import { usePageContext } from '@/lib/page-context';
8
8
 
9
9
  interface ApiPageProps {
10
- slug: string[]
10
+ slug: string[];
11
11
  }
12
12
 
13
13
  export function ApiPage({ slug }: ApiPageProps) {
14
- const { config, apiSpecs } = usePageContext()
14
+ const { config, apiSpecs } = usePageContext();
15
15
 
16
16
  if (slug.length === 0) {
17
17
  return (
18
18
  <>
19
19
  <Head
20
- title="API Reference"
20
+ title='API Reference'
21
21
  description={`API documentation for ${config.title}`}
22
22
  config={config}
23
23
  />
24
24
  <ApiLanding specs={apiSpecs} />
25
25
  </>
26
- )
26
+ );
27
27
  }
28
28
 
29
- const match = findApiOperation(apiSpecs, slug)
30
- if (!match) return null
29
+ const match = findApiOperation(apiSpecs, slug);
30
+ if (!match) return null;
31
31
 
32
- const operation = match.operation as OpenAPIV3.OperationObject
33
- const title = operation.summary ?? `${match.method.toUpperCase()} ${match.path}`
32
+ const operation = match.operation as OpenAPIV3.OperationObject;
33
+ const title =
34
+ operation.summary ?? `${match.method.toUpperCase()} ${match.path}`;
34
35
 
35
36
  return (
36
37
  <>
37
- <Head
38
- title={title}
39
- description={operation.description}
40
- config={config}
41
- />
38
+ <Head title={title} description={operation.description} config={config} />
42
39
  <EndpointPage
43
40
  method={match.method}
44
41
  path={match.path}
@@ -48,21 +45,29 @@ export function ApiPage({ slug }: ApiPageProps) {
48
45
  auth={match.spec.auth}
49
46
  />
50
47
  </>
51
- )
48
+ );
52
49
  }
53
50
 
54
51
  function ApiLanding({ specs }: { specs: ApiSpec[] }) {
55
52
  return (
56
- <Flex direction="column" gap="large" style={{ padding: 'var(--rs-space-7)' }}>
57
- <Headline size="medium" as="h1">API Reference</Headline>
58
- {specs.map((spec) => (
59
- <Flex key={spec.name} direction="column" gap="small">
60
- <Headline size="small" as="h2">{spec.name}</Headline>
53
+ <Flex
54
+ direction='column'
55
+ gap='large'
56
+ style={{ padding: 'var(--rs-space-7)' }}
57
+ >
58
+ <Headline size='medium' as='h1'>
59
+ API Reference
60
+ </Headline>
61
+ {specs.map(spec => (
62
+ <Flex key={spec.name} direction='column' gap='small'>
63
+ <Headline size='small' as='h2'>
64
+ {spec.name}
65
+ </Headline>
61
66
  {spec.document.info.description && (
62
67
  <Text size={3}>{spec.document.info.description}</Text>
63
68
  )}
64
69
  </Flex>
65
70
  ))}
66
71
  </Flex>
67
- )
72
+ );
68
73
  }
@@ -1,18 +1,18 @@
1
- import type { ReactNode } from 'react'
2
- import { usePageContext } from '@/lib/page-context'
3
- import { getTheme } from '@/themes/registry'
1
+ import type { ReactNode } from 'react';
2
+ import { usePageContext } from '@/lib/page-context';
3
+ import { getTheme } from '@/themes/registry';
4
4
 
5
5
  interface DocsLayoutProps {
6
- children: ReactNode
6
+ children: ReactNode;
7
7
  }
8
8
 
9
9
  export function DocsLayout({ children }: DocsLayoutProps) {
10
- const { config, tree } = usePageContext()
11
- const { Layout, className } = getTheme(config.theme?.name)
10
+ const { config, tree } = usePageContext();
11
+ const { Layout, className } = getTheme(config.theme?.name);
12
12
 
13
13
  return (
14
14
  <Layout config={config} tree={tree} classNames={{ layout: className }}>
15
15
  {children}
16
16
  </Layout>
17
- )
17
+ );
18
18
  }
@@ -1,18 +1,18 @@
1
- import { usePageContext } from '@/lib/page-context'
2
- import { getTheme } from '@/themes/registry'
3
- import { Head } from '@/lib/head'
1
+ import { Head } from '@/lib/head';
2
+ import { usePageContext } from '@/lib/page-context';
3
+ import { getTheme } from '@/themes/registry';
4
4
 
5
5
  interface DocsPageProps {
6
- slug: string[]
6
+ slug: string[];
7
7
  }
8
8
 
9
9
  export function DocsPage({ slug }: DocsPageProps) {
10
- const { config, tree, page } = usePageContext()
10
+ const { config, tree, page } = usePageContext();
11
11
 
12
- if (!page) return null
12
+ if (!page) return null;
13
13
 
14
- const { Page } = getTheme(config.theme?.name)
15
- const pageUrl = config.url ? `${config.url}/${slug.join('/')}` : undefined
14
+ const { Page } = getTheme(config.theme?.name);
15
+ const pageUrl = config.url ? `${config.url}/${slug.join('/')}` : undefined;
16
16
 
17
17
  return (
18
18
  <>
@@ -25,7 +25,7 @@ export function DocsPage({ slug }: DocsPageProps) {
25
25
  '@type': 'Article',
26
26
  headline: page.frontmatter.title,
27
27
  description: page.frontmatter.description,
28
- ...(pageUrl && { url: pageUrl }),
28
+ ...(pageUrl && { url: pageUrl })
29
29
  }}
30
30
  />
31
31
  <Page
@@ -33,11 +33,11 @@ export function DocsPage({ slug }: DocsPageProps) {
33
33
  slug,
34
34
  frontmatter: page.frontmatter,
35
35
  content: page.content,
36
- toc: [],
36
+ toc: page.toc
37
37
  }}
38
38
  config={config}
39
39
  tree={tree}
40
40
  />
41
41
  </>
42
- )
42
+ );
43
43
  }
@@ -1,10 +1,17 @@
1
- import { Flex, Headline, Text } from '@raystack/apsara'
1
+ import { Flex, Headline, Text } from '@raystack/apsara';
2
2
 
3
3
  export function NotFound() {
4
4
  return (
5
- <Flex direction="column" align="center" justify="center" style={{ minHeight: '60vh' }}>
6
- <Headline size="large" as="h1">404</Headline>
5
+ <Flex
6
+ direction='column'
7
+ align='center'
8
+ justify='center'
9
+ style={{ minHeight: '60vh' }}
10
+ >
11
+ <Headline size='large' as='h1'>
12
+ 404
13
+ </Headline>
7
14
  <Text size={3}>Page not found</Text>
8
15
  </Flex>
9
- )
16
+ );
10
17
  }
@@ -1,29 +1,33 @@
1
- import '@raystack/apsara/normalize.css'
2
- import '@raystack/apsara/style.css'
3
- import { ThemeProvider } from '@raystack/apsara'
4
- import { useLocation } from 'react-router-dom'
5
- import { usePageContext } from '@/lib/page-context'
6
- import { Head } from '@/lib/head'
7
- import { DocsLayout } from '@/pages/DocsLayout'
8
- import { DocsPage } from '@/pages/DocsPage'
9
- import { ApiLayout } from '@/pages/ApiLayout'
10
- import { ApiPage } from '@/pages/ApiPage'
11
- import type { ChronicleConfig } from '@/types'
1
+ import '@raystack/apsara/normalize.css';
2
+ import '@raystack/apsara/style.css';
3
+ import { ThemeProvider } from '@raystack/apsara';
4
+ import { useLocation } from 'react-router';
5
+ import { Head } from '@/lib/head';
6
+ import { usePageContext } from '@/lib/page-context';
7
+ import { ApiLayout } from '@/pages/ApiLayout';
8
+ import { ApiPage } from '@/pages/ApiPage';
9
+ import { DocsLayout } from '@/pages/DocsLayout';
10
+ import { DocsPage } from '@/pages/DocsPage';
11
+ import type { ChronicleConfig } from '@/types';
12
12
 
13
13
  function resolveRoute(pathname: string) {
14
14
  if (pathname.startsWith('/apis')) {
15
- const slug = pathname.replace(/^\/apis\/?/, '').split('/').filter(Boolean)
16
- return { type: 'api' as const, slug }
15
+ const slug = pathname
16
+ .replace(/^\/apis\/?/, '')
17
+ .split('/')
18
+ .filter(Boolean);
19
+ return { type: 'api' as const, slug };
17
20
  }
18
21
 
19
- const slug = pathname === '/' ? [] : pathname.slice(1).split('/').filter(Boolean)
20
- return { type: 'docs' as const, slug }
22
+ const slug =
23
+ pathname === '/' ? [] : pathname.slice(1).split('/').filter(Boolean);
24
+ return { type: 'docs' as const, slug };
21
25
  }
22
26
 
23
27
  export function App() {
24
- const { pathname } = useLocation()
25
- const { config } = usePageContext()
26
- const route = resolveRoute(pathname)
28
+ const { pathname } = useLocation();
29
+ const { config } = usePageContext();
30
+ const route = resolveRoute(pathname);
27
31
 
28
32
  return (
29
33
  <ThemeProvider enableSystem>
@@ -38,7 +42,7 @@ export function App() {
38
42
  </DocsLayout>
39
43
  )}
40
44
  </ThemeProvider>
41
- )
45
+ );
42
46
  }
43
47
 
44
48
  function RootHead({ config }: { config: ChronicleConfig }) {
@@ -47,13 +51,17 @@ function RootHead({ config }: { config: ChronicleConfig }) {
47
51
  title={config.title}
48
52
  description={config.description}
49
53
  config={config}
50
- jsonLd={config.url ? {
51
- '@context': 'https://schema.org',
52
- '@type': 'WebSite',
53
- name: config.title,
54
- description: config.description,
55
- url: config.url,
56
- } : undefined}
54
+ jsonLd={
55
+ config.url
56
+ ? {
57
+ '@context': 'https://schema.org',
58
+ '@type': 'WebSite',
59
+ name: config.title,
60
+ description: config.description,
61
+ url: config.url
62
+ }
63
+ : undefined
64
+ }
57
65
  />
58
- )
66
+ );
59
67
  }