@kuckit/docs-module 0.1.1

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.
@@ -0,0 +1,62 @@
1
+ import { KuckitClientModuleContext } from "@kuckit/sdk-react";
2
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
3
+
4
+ //#region src/client/pages/DocsPage.d.ts
5
+ declare function DocsPage(): react_jsx_runtime0.JSX.Element | null;
6
+ //#endregion
7
+ //#region src/client/types.d.ts
8
+ interface DocSlug {
9
+ readonly segments: readonly string[];
10
+ readonly versionId?: string;
11
+ }
12
+ interface DocFrontmatter {
13
+ readonly title: string;
14
+ readonly description?: string;
15
+ readonly order?: number;
16
+ readonly group?: string;
17
+ readonly icon?: string;
18
+ readonly tags?: readonly string[];
19
+ readonly sidebarHidden?: boolean;
20
+ readonly searchHidden?: boolean;
21
+ }
22
+ interface DocTocItem {
23
+ readonly id: string;
24
+ readonly depth: number;
25
+ readonly text: string;
26
+ }
27
+ interface DocTreeNode {
28
+ readonly slug: DocSlug;
29
+ readonly path: string;
30
+ readonly title: string;
31
+ readonly group?: string;
32
+ readonly icon?: string;
33
+ readonly order: number;
34
+ readonly children: readonly DocTreeNode[];
35
+ readonly isIndex: boolean;
36
+ readonly versionId?: string;
37
+ }
38
+ interface CompiledMdxPayload {
39
+ readonly kind: 'mdx-serialized';
40
+ readonly code: string;
41
+ readonly scope: Record<string, unknown>;
42
+ }
43
+ interface DocPage {
44
+ readonly slug: DocSlug;
45
+ readonly path: string;
46
+ readonly frontmatter: DocFrontmatter;
47
+ readonly toc: readonly DocTocItem[];
48
+ readonly compiled: CompiledMdxPayload;
49
+ readonly lastModified?: string;
50
+ readonly versionId?: string;
51
+ }
52
+ //#endregion
53
+ //#region src/client/index.d.ts
54
+ declare const kuckitClientModule: {
55
+ id: string;
56
+ displayName: string;
57
+ version: string;
58
+ register(ctx: KuckitClientModuleContext): void;
59
+ };
60
+ //#endregion
61
+ export { CompiledMdxPayload, DocFrontmatter, DocPage, DocSlug, DocTocItem, DocTreeNode, DocsPage, kuckitClientModule };
62
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,220 @@
1
+ import { defineKuckitClientModule, useRpc } from "@kuckit/sdk-react";
2
+ import { Suspense, createContext, useContext, useEffect, useMemo, useState } from "react";
3
+ import { useRouterState } from "@tanstack/react-router";
4
+ import { executeMdxSync } from "@fumadocs/mdx-remote/client";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+
7
+ //#region src/client/pages/DocsPage.tsx
8
+ const DocsConfigContext = createContext({ basePath: "/docs" });
9
+ function DocsPage() {
10
+ const rpc = useRpc();
11
+ const pathname = useRouterState().location.pathname;
12
+ const { basePath } = useContext(DocsConfigContext);
13
+ const slugPath = pathname.replace(/^\/docs\/?/, "");
14
+ const slug = slugPath ? slugPath.split("/").filter(Boolean) : [];
15
+ const [tree, setTree] = useState([]);
16
+ const [treeLoading, setTreeLoading] = useState(true);
17
+ const [page, setPage] = useState(null);
18
+ const [loading, setLoading] = useState(true);
19
+ const [error, setError] = useState(null);
20
+ useEffect(() => {
21
+ rpc.docs.getTree({}).then((data) => {
22
+ setTree(data);
23
+ setTreeLoading(false);
24
+ });
25
+ }, [rpc]);
26
+ useEffect(() => {
27
+ async function load() {
28
+ setLoading(true);
29
+ setError(null);
30
+ try {
31
+ const pageData = await rpc.docs.getPage({ slug });
32
+ if (!pageData) setError({
33
+ type: "not-found",
34
+ message: "The documentation page you're looking for doesn't exist."
35
+ });
36
+ else setPage(pageData);
37
+ } catch (err) {
38
+ if (err?.data?.code === "COMPILE_ERROR") setError({
39
+ type: "compile-error",
40
+ message: "Failed to render page. Check MDX syntax."
41
+ });
42
+ else setError({
43
+ type: "load-error",
44
+ message: "Unable to load documentation content."
45
+ });
46
+ } finally {
47
+ setLoading(false);
48
+ }
49
+ }
50
+ load();
51
+ }, [slug.join("/"), rpc]);
52
+ const MdxContent = useMemo(() => {
53
+ if (!page) return null;
54
+ return executeMdxSync(page.compiled.code, { scope: page.compiled.scope }).default;
55
+ }, [page]);
56
+ if (loading || treeLoading) return /* @__PURE__ */ jsx("div", {
57
+ className: "flex items-center justify-center min-h-screen bg-background",
58
+ children: /* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-primary" })
59
+ });
60
+ if (error) return /* @__PURE__ */ jsxs("div", {
61
+ className: "flex flex-col items-center justify-center min-h-screen bg-background",
62
+ children: [
63
+ /* @__PURE__ */ jsx("h1", {
64
+ className: "text-2xl font-bold",
65
+ children: error.type === "not-found" ? "Page Not Found" : "Error"
66
+ }),
67
+ /* @__PURE__ */ jsx("p", {
68
+ className: "text-muted-foreground mt-2",
69
+ children: error.message
70
+ }),
71
+ /* @__PURE__ */ jsx("a", {
72
+ href: "/docs",
73
+ className: "mt-4 text-primary hover:underline",
74
+ children: "← Back to Documentation"
75
+ })
76
+ ]
77
+ });
78
+ if (!page || !MdxContent) return null;
79
+ const currentPath = slug.length > 0 ? `${basePath}/${slug.join("/")}` : basePath;
80
+ return /* @__PURE__ */ jsxs("div", {
81
+ className: "flex min-h-screen bg-background text-foreground",
82
+ children: [
83
+ /* @__PURE__ */ jsxs("aside", {
84
+ className: "w-64 shrink-0 border-r border-border hidden md:block sticky top-0 h-screen overflow-y-auto",
85
+ children: [/* @__PURE__ */ jsx("div", {
86
+ className: "p-6",
87
+ children: /* @__PURE__ */ jsx("a", {
88
+ href: "/docs",
89
+ className: "text-lg font-semibold text-foreground hover:text-primary transition-colors",
90
+ children: "Documentation"
91
+ })
92
+ }), /* @__PURE__ */ jsx("nav", {
93
+ className: "px-4 pb-6",
94
+ children: /* @__PURE__ */ jsx(DocsSidebar, {
95
+ tree,
96
+ currentPath,
97
+ basePath
98
+ })
99
+ })]
100
+ }),
101
+ /* @__PURE__ */ jsx("main", {
102
+ className: "flex-1 min-w-0",
103
+ children: /* @__PURE__ */ jsx("div", {
104
+ className: "max-w-3xl mx-auto px-8 py-12",
105
+ children: /* @__PURE__ */ jsx(Suspense, {
106
+ fallback: /* @__PURE__ */ jsx("div", {
107
+ className: "p-8 text-muted-foreground",
108
+ children: "Loading..."
109
+ }),
110
+ children: /* @__PURE__ */ jsxs("article", {
111
+ className: "prose prose-neutral dark:prose-invert max-w-none",
112
+ children: [
113
+ /* @__PURE__ */ jsx("h1", { children: page.frontmatter.title }),
114
+ page.frontmatter.description && /* @__PURE__ */ jsx("p", {
115
+ className: "lead",
116
+ children: page.frontmatter.description
117
+ }),
118
+ /* @__PURE__ */ jsx(MdxContent, {})
119
+ ]
120
+ })
121
+ })
122
+ })
123
+ }),
124
+ /* @__PURE__ */ jsx("aside", {
125
+ className: "w-56 shrink-0 hidden lg:block sticky top-0 h-screen overflow-y-auto border-l border-border",
126
+ children: /* @__PURE__ */ jsx("div", {
127
+ className: "p-6",
128
+ children: /* @__PURE__ */ jsx(DocsToc, { toc: page.toc })
129
+ })
130
+ })
131
+ ]
132
+ });
133
+ }
134
+ function DocsSidebar({ tree, currentPath, basePath }) {
135
+ return /* @__PURE__ */ jsx("nav", {
136
+ className: "space-y-1",
137
+ children: tree.map((node) => /* @__PURE__ */ jsx(SidebarNode, {
138
+ node,
139
+ currentPath,
140
+ basePath,
141
+ depth: 0
142
+ }, node.path))
143
+ });
144
+ }
145
+ function SidebarNode({ node, currentPath, basePath, depth }) {
146
+ const nodePath = `${basePath}/${node.slug.segments.join("/")}`;
147
+ const isActive = currentPath === nodePath;
148
+ const hasChildren = node.children && node.children.length > 0;
149
+ return /* @__PURE__ */ jsxs("div", {
150
+ className: depth > 0 ? "ml-3 border-l border-border/50 pl-3" : "",
151
+ children: [/* @__PURE__ */ jsx("a", {
152
+ href: nodePath,
153
+ className: `block px-3 py-2 rounded-md text-sm transition-colors ${isActive ? "bg-primary text-primary-foreground font-medium" : "text-muted-foreground hover:text-foreground hover:bg-muted"}`,
154
+ children: node.title
155
+ }), hasChildren && /* @__PURE__ */ jsx("div", {
156
+ className: "mt-1 space-y-1",
157
+ children: node.children.map((child) => /* @__PURE__ */ jsx(SidebarNode, {
158
+ node: child,
159
+ currentPath,
160
+ basePath,
161
+ depth: depth + 1
162
+ }, child.path))
163
+ })]
164
+ });
165
+ }
166
+ function DocsToc({ toc }) {
167
+ if (!toc || toc.length === 0) return null;
168
+ return /* @__PURE__ */ jsxs("nav", {
169
+ className: "space-y-1",
170
+ children: [/* @__PURE__ */ jsx("h3", {
171
+ className: "font-semibold text-sm mb-3 text-foreground",
172
+ children: "On this page"
173
+ }), toc.map((item) => /* @__PURE__ */ jsx("a", {
174
+ href: `#${item.id}`,
175
+ className: "block text-sm text-muted-foreground hover:text-foreground transition-colors py-1",
176
+ style: { paddingLeft: `${(item.depth - 2) * 12}px` },
177
+ children: item.text
178
+ }, item.id))]
179
+ });
180
+ }
181
+
182
+ //#endregion
183
+ //#region src/client/index.ts
184
+ const kuckitClientModule = defineKuckitClientModule({
185
+ id: "kuckit.docs",
186
+ displayName: "Documentation",
187
+ version: "0.1.0",
188
+ register(ctx) {
189
+ ctx.registerComponent("DocsPage", DocsPage);
190
+ ctx.addRoute({
191
+ id: "docs-index",
192
+ path: "/docs",
193
+ component: DocsPage,
194
+ meta: {
195
+ title: "Documentation",
196
+ requiresAuth: false
197
+ }
198
+ });
199
+ ctx.addRoute({
200
+ id: "docs-page",
201
+ path: "/docs/$",
202
+ component: DocsPage,
203
+ meta: {
204
+ title: "Documentation",
205
+ requiresAuth: false
206
+ }
207
+ });
208
+ ctx.addNavItem({
209
+ id: "nav-docs",
210
+ label: "Docs",
211
+ path: "/docs",
212
+ icon: "book-open",
213
+ order: 20
214
+ });
215
+ }
216
+ });
217
+
218
+ //#endregion
219
+ export { DocsPage, kuckitClientModule };
220
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["err: unknown"],"sources":["../../src/client/pages/DocsPage.tsx","../../src/client/index.ts"],"sourcesContent":["import { Suspense, useEffect, useState, createContext, useContext, useMemo } from 'react'\nimport { useRouterState } from '@tanstack/react-router'\nimport { useRpc } from '@kuckit/sdk-react'\nimport { executeMdxSync } from '@fumadocs/mdx-remote/client'\nimport type { DocTreeNode, DocPage as DocPageType, DocTocItem } from '../types'\n\ninterface DocsRpc {\n\tdocs: {\n\t\tgetTree: (input: { versionId?: string }) => Promise<DocTreeNode[]>\n\t\tgetPage: (input: { slug: string[]; versionId?: string }) => Promise<DocPageType | null>\n\t}\n}\n\nconst DocsConfigContext = createContext<{ basePath: string }>({ basePath: '/docs' })\n\nexport function DocsPage() {\n\tconst rpc = useRpc<DocsRpc>()\n\tconst routerState = useRouterState()\n\tconst pathname = routerState.location.pathname\n\n\tconst { basePath } = useContext(DocsConfigContext)\n\tconst slugPath = pathname.replace(/^\\/docs\\/?/, '')\n\tconst slug = slugPath ? slugPath.split('/').filter(Boolean) : []\n\n\tconst [tree, setTree] = useState<DocTreeNode[]>([])\n\tconst [treeLoading, setTreeLoading] = useState(true)\n\tconst [page, setPage] = useState<DocPageType | null>(null)\n\tconst [loading, setLoading] = useState(true)\n\tconst [error, setError] = useState<{\n\t\ttype: 'not-found' | 'compile-error' | 'load-error'\n\t\tmessage: string\n\t} | null>(null)\n\n\tuseEffect(() => {\n\t\trpc.docs.getTree({}).then((data) => {\n\t\t\tsetTree(data)\n\t\t\tsetTreeLoading(false)\n\t\t})\n\t}, [rpc])\n\n\tuseEffect(() => {\n\t\tasync function load() {\n\t\t\tsetLoading(true)\n\t\t\tsetError(null)\n\t\t\ttry {\n\t\t\t\tconst pageData = await rpc.docs.getPage({ slug })\n\t\t\t\tif (!pageData) {\n\t\t\t\t\tsetError({\n\t\t\t\t\t\ttype: 'not-found',\n\t\t\t\t\t\tmessage: \"The documentation page you're looking for doesn't exist.\",\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tsetPage(pageData)\n\t\t\t\t}\n\t\t\t} catch (err: unknown) {\n\t\t\t\tconst errorData = err as { data?: { code?: string } }\n\t\t\t\tif (errorData?.data?.code === 'COMPILE_ERROR') {\n\t\t\t\t\tsetError({ type: 'compile-error', message: 'Failed to render page. Check MDX syntax.' })\n\t\t\t\t} else {\n\t\t\t\t\tsetError({ type: 'load-error', message: 'Unable to load documentation content.' })\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tsetLoading(false)\n\t\t\t}\n\t\t}\n\t\tload()\n\t}, [slug.join('/'), rpc])\n\n\tconst MdxContent = useMemo(() => {\n\t\tif (!page) return null\n\t\tconst result = executeMdxSync(page.compiled.code, {\n\t\t\tscope: page.compiled.scope,\n\t\t})\n\t\treturn result.default\n\t}, [page])\n\n\tif (loading || treeLoading) {\n\t\treturn (\n\t\t\t<div className=\"flex items-center justify-center min-h-screen bg-background\">\n\t\t\t\t<div className=\"animate-spin rounded-full h-8 w-8 border-b-2 border-primary\" />\n\t\t\t</div>\n\t\t)\n\t}\n\n\tif (error) {\n\t\treturn (\n\t\t\t<div className=\"flex flex-col items-center justify-center min-h-screen bg-background\">\n\t\t\t\t<h1 className=\"text-2xl font-bold\">\n\t\t\t\t\t{error.type === 'not-found' ? 'Page Not Found' : 'Error'}\n\t\t\t\t</h1>\n\t\t\t\t<p className=\"text-muted-foreground mt-2\">{error.message}</p>\n\t\t\t\t<a href=\"/docs\" className=\"mt-4 text-primary hover:underline\">\n\t\t\t\t\t← Back to Documentation\n\t\t\t\t</a>\n\t\t\t</div>\n\t\t)\n\t}\n\n\tif (!page || !MdxContent) return null\n\n\tconst currentPath = slug.length > 0 ? `${basePath}/${slug.join('/')}` : basePath\n\n\treturn (\n\t\t<div className=\"flex min-h-screen bg-background text-foreground\">\n\t\t\t{/* Sidebar */}\n\t\t\t<aside className=\"w-64 shrink-0 border-r border-border hidden md:block sticky top-0 h-screen overflow-y-auto\">\n\t\t\t\t<div className=\"p-6\">\n\t\t\t\t\t<a\n\t\t\t\t\t\thref=\"/docs\"\n\t\t\t\t\t\tclassName=\"text-lg font-semibold text-foreground hover:text-primary transition-colors\"\n\t\t\t\t\t>\n\t\t\t\t\t\tDocumentation\n\t\t\t\t\t</a>\n\t\t\t\t</div>\n\t\t\t\t<nav className=\"px-4 pb-6\">\n\t\t\t\t\t<DocsSidebar tree={tree} currentPath={currentPath} basePath={basePath} />\n\t\t\t\t</nav>\n\t\t\t</aside>\n\n\t\t\t{/* Main content */}\n\t\t\t<main className=\"flex-1 min-w-0\">\n\t\t\t\t<div className=\"max-w-3xl mx-auto px-8 py-12\">\n\t\t\t\t\t<Suspense fallback={<div className=\"p-8 text-muted-foreground\">Loading...</div>}>\n\t\t\t\t\t\t<article className=\"prose prose-neutral dark:prose-invert max-w-none\">\n\t\t\t\t\t\t\t<h1>{page.frontmatter.title}</h1>\n\t\t\t\t\t\t\t{page.frontmatter.description && (\n\t\t\t\t\t\t\t\t<p className=\"lead\">{page.frontmatter.description}</p>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t<MdxContent />\n\t\t\t\t\t\t</article>\n\t\t\t\t\t</Suspense>\n\t\t\t\t</div>\n\t\t\t</main>\n\n\t\t\t{/* Table of contents */}\n\t\t\t<aside className=\"w-56 shrink-0 hidden lg:block sticky top-0 h-screen overflow-y-auto border-l border-border\">\n\t\t\t\t<div className=\"p-6\">\n\t\t\t\t\t<DocsToc toc={page.toc as DocTocItem[]} />\n\t\t\t\t</div>\n\t\t\t</aside>\n\t\t</div>\n\t)\n}\n\nfunction DocsSidebar({\n\ttree,\n\tcurrentPath,\n\tbasePath,\n}: {\n\ttree: DocTreeNode[]\n\tcurrentPath: string\n\tbasePath: string\n}) {\n\treturn (\n\t\t<nav className=\"space-y-1\">\n\t\t\t{tree.map((node) => (\n\t\t\t\t<SidebarNode\n\t\t\t\t\tkey={node.path}\n\t\t\t\t\tnode={node}\n\t\t\t\t\tcurrentPath={currentPath}\n\t\t\t\t\tbasePath={basePath}\n\t\t\t\t\tdepth={0}\n\t\t\t\t/>\n\t\t\t))}\n\t\t</nav>\n\t)\n}\n\nfunction SidebarNode({\n\tnode,\n\tcurrentPath,\n\tbasePath,\n\tdepth,\n}: {\n\tnode: DocTreeNode\n\tcurrentPath: string\n\tbasePath: string\n\tdepth: number\n}) {\n\tconst nodePath = `${basePath}/${node.slug.segments.join('/')}`\n\tconst isActive = currentPath === nodePath\n\tconst hasChildren = node.children && node.children.length > 0\n\n\treturn (\n\t\t<div className={depth > 0 ? 'ml-3 border-l border-border/50 pl-3' : ''}>\n\t\t\t<a\n\t\t\t\thref={nodePath}\n\t\t\t\tclassName={`block px-3 py-2 rounded-md text-sm transition-colors ${\n\t\t\t\t\tisActive\n\t\t\t\t\t\t? 'bg-primary text-primary-foreground font-medium'\n\t\t\t\t\t\t: 'text-muted-foreground hover:text-foreground hover:bg-muted'\n\t\t\t\t}`}\n\t\t\t>\n\t\t\t\t{node.title}\n\t\t\t</a>\n\t\t\t{hasChildren && (\n\t\t\t\t<div className=\"mt-1 space-y-1\">\n\t\t\t\t\t{node.children!.map((child) => (\n\t\t\t\t\t\t<SidebarNode\n\t\t\t\t\t\t\tkey={child.path}\n\t\t\t\t\t\t\tnode={child as DocTreeNode}\n\t\t\t\t\t\t\tcurrentPath={currentPath}\n\t\t\t\t\t\t\tbasePath={basePath}\n\t\t\t\t\t\t\tdepth={depth + 1}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t)\n}\n\nfunction DocsToc({ toc }: { toc: DocTocItem[] }) {\n\tif (!toc || toc.length === 0) return null\n\n\treturn (\n\t\t<nav className=\"space-y-1\">\n\t\t\t<h3 className=\"font-semibold text-sm mb-3 text-foreground\">On this page</h3>\n\t\t\t{toc.map((item) => (\n\t\t\t\t<a\n\t\t\t\t\tkey={item.id}\n\t\t\t\t\thref={`#${item.id}`}\n\t\t\t\t\tclassName=\"block text-sm text-muted-foreground hover:text-foreground transition-colors py-1\"\n\t\t\t\t\tstyle={{ paddingLeft: `${(item.depth - 2) * 12}px` }}\n\t\t\t\t>\n\t\t\t\t\t{item.text}\n\t\t\t\t</a>\n\t\t\t))}\n\t\t</nav>\n\t)\n}\n","import { defineKuckitClientModule, type KuckitClientModuleContext } from '@kuckit/sdk-react'\nimport { DocsPage } from './pages/DocsPage'\n\nexport const kuckitClientModule = defineKuckitClientModule({\n\tid: 'kuckit.docs',\n\tdisplayName: 'Documentation',\n\tversion: '0.1.0',\n\n\tregister(ctx: KuckitClientModuleContext) {\n\t\tctx.registerComponent('DocsPage', DocsPage)\n\n\t\tctx.addRoute({\n\t\t\tid: 'docs-index',\n\t\t\tpath: '/docs',\n\t\t\tcomponent: DocsPage,\n\t\t\tmeta: {\n\t\t\t\ttitle: 'Documentation',\n\t\t\t\trequiresAuth: false,\n\t\t\t},\n\t\t})\n\n\t\tctx.addRoute({\n\t\t\tid: 'docs-page',\n\t\t\tpath: '/docs/$',\n\t\t\tcomponent: DocsPage,\n\t\t\tmeta: {\n\t\t\t\ttitle: 'Documentation',\n\t\t\t\trequiresAuth: false,\n\t\t\t},\n\t\t})\n\n\t\tctx.addNavItem({\n\t\t\tid: 'nav-docs',\n\t\t\tlabel: 'Docs',\n\t\t\tpath: '/docs',\n\t\t\ticon: 'book-open',\n\t\t\torder: 20,\n\t\t})\n\t},\n})\n\nexport { DocsPage } from './pages/DocsPage'\nexport type * from './types'\n"],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import * as _kuckit_sdk0 from "@kuckit/sdk";
2
+ import { z } from "zod";
3
+
4
+ //#region src/server/config.d.ts
5
+
6
+ declare const docsModuleConfigSchema: z.ZodObject<{
7
+ docsDir: z.ZodDefault<z.ZodString>;
8
+ basePath: z.ZodDefault<z.ZodString>;
9
+ enableSearch: z.ZodDefault<z.ZodBoolean>;
10
+ cacheTtlSeconds: z.ZodDefault<z.ZodNumber>;
11
+ devMode: z.ZodOptional<z.ZodBoolean>;
12
+ versions: z.ZodOptional<z.ZodArray<z.ZodObject<{
13
+ id: z.ZodString;
14
+ label: z.ZodString;
15
+ pathSegment: z.ZodString;
16
+ default: z.ZodOptional<z.ZodBoolean>;
17
+ hidden: z.ZodOptional<z.ZodBoolean>;
18
+ }, z.core.$strip>>>;
19
+ fumadocs: z.ZodOptional<z.ZodObject<{
20
+ title: z.ZodOptional<z.ZodString>;
21
+ description: z.ZodOptional<z.ZodString>;
22
+ logoSrc: z.ZodOptional<z.ZodString>;
23
+ primaryColor: z.ZodOptional<z.ZodString>;
24
+ tocDepth: z.ZodDefault<z.ZodNumber>;
25
+ }, z.core.$strip>>;
26
+ }, z.core.$strip>;
27
+ type DocsModuleConfig = z.infer<typeof docsModuleConfigSchema>;
28
+ //#endregion
29
+ //#region src/server/module.d.ts
30
+ declare const kuckitModule: _kuckit_sdk0.KuckitModuleDefinition<{
31
+ docsDir: string;
32
+ basePath: string;
33
+ enableSearch: boolean;
34
+ cacheTtlSeconds: number;
35
+ devMode?: boolean | undefined;
36
+ versions?: {
37
+ id: string;
38
+ label: string;
39
+ pathSegment: string;
40
+ default?: boolean | undefined;
41
+ hidden?: boolean | undefined;
42
+ }[] | undefined;
43
+ fumadocs?: {
44
+ tocDepth: number;
45
+ title?: string | undefined;
46
+ description?: string | undefined;
47
+ logoSrc?: string | undefined;
48
+ primaryColor?: string | undefined;
49
+ } | undefined;
50
+ }>;
51
+ //#endregion
52
+ export { type DocsModuleConfig, kuckitModule };
53
+ //# sourceMappingURL=module.d.ts.map
@@ -0,0 +1,525 @@
1
+ import * as path from "node:path";
2
+ import { asFunction, defineKuckitModule } from "@kuckit/sdk";
3
+ import { z } from "zod";
4
+ import * as fs from "node:fs/promises";
5
+ import matter from "gray-matter";
6
+ import { createCompiler, parseFrontmatter } from "@fumadocs/mdx-remote";
7
+ import { publicProcedure } from "@kuckit/api";
8
+
9
+ //#region src/server/config.ts
10
+ const docsVersionConfigSchema = z.object({
11
+ id: z.string(),
12
+ label: z.string(),
13
+ pathSegment: z.string(),
14
+ default: z.boolean().optional(),
15
+ hidden: z.boolean().optional()
16
+ });
17
+ const docsModuleConfigSchema = z.object({
18
+ docsDir: z.string().default("content/docs"),
19
+ basePath: z.string().default("/docs"),
20
+ enableSearch: z.boolean().default(true),
21
+ cacheTtlSeconds: z.number().default(300),
22
+ devMode: z.boolean().optional(),
23
+ versions: z.array(docsVersionConfigSchema).optional(),
24
+ fumadocs: z.object({
25
+ title: z.string().optional(),
26
+ description: z.string().optional(),
27
+ logoSrc: z.string().optional(),
28
+ primaryColor: z.string().optional(),
29
+ tocDepth: z.number().default(3)
30
+ }).optional()
31
+ });
32
+
33
+ //#endregion
34
+ //#region src/server/domain/doc-node.ts
35
+ const docSlugSchema = z.object({
36
+ segments: z.array(z.string()),
37
+ versionId: z.string().optional()
38
+ });
39
+ const docFrontmatterSchema = z.object({
40
+ title: z.string(),
41
+ description: z.string().optional(),
42
+ order: z.number().optional(),
43
+ group: z.string().optional(),
44
+ icon: z.string().optional(),
45
+ sidebarHidden: z.boolean().optional(),
46
+ searchHidden: z.boolean().optional(),
47
+ tags: z.array(z.string()).optional()
48
+ });
49
+ const docTocItemSchema = z.object({
50
+ id: z.string(),
51
+ text: z.string(),
52
+ depth: z.number()
53
+ });
54
+
55
+ //#endregion
56
+ //#region src/server/adapters/filesystem-docs.repository.ts
57
+ const FRONTMATTER_HEAD_BYTES = 2048;
58
+ function makeFilesystemDocsRepository(options) {
59
+ const { docsDir, versions } = options;
60
+ const frontmatterCache = /* @__PURE__ */ new Map();
61
+ function validateSlug(slug) {
62
+ if (slug.some((segment) => segment === ".." || segment === "" || segment.startsWith("/"))) throw new Error("Invalid slug: path traversal detected");
63
+ }
64
+ function getDocsPath(versionId) {
65
+ if (versionId && versions) {
66
+ const version = versions.find((v) => v.id === versionId);
67
+ if (version) return path.join(docsDir, version.pathSegment);
68
+ }
69
+ return docsDir;
70
+ }
71
+ async function findMdxFile(basePath, slug) {
72
+ const slugPath = slug.join("/");
73
+ const candidates = [
74
+ path.join(basePath, `${slugPath}.mdx`),
75
+ path.join(basePath, `${slugPath}.md`),
76
+ path.join(basePath, slugPath, "index.mdx"),
77
+ path.join(basePath, slugPath, "index.md")
78
+ ];
79
+ if (slug.length === 0) candidates.unshift(path.join(basePath, "index.mdx"), path.join(basePath, "index.md"));
80
+ for (const candidate of candidates) try {
81
+ const resolved = path.resolve(candidate);
82
+ if (!resolved.startsWith(path.resolve(docsDir))) continue;
83
+ await fs.access(resolved);
84
+ return resolved;
85
+ } catch {
86
+ continue;
87
+ }
88
+ return null;
89
+ }
90
+ async function buildTree(dir, parentSlug = [], versionId) {
91
+ const nodes = [];
92
+ let entries;
93
+ try {
94
+ entries = await fs.readdir(dir, { withFileTypes: true });
95
+ } catch {
96
+ return [];
97
+ }
98
+ for (const entry of entries) {
99
+ const entryName = entry.name;
100
+ const fullPath = path.join(dir, entryName);
101
+ if (entry.isDirectory()) {
102
+ const childSlug = [...parentSlug, entryName];
103
+ const children = await buildTree(fullPath, childSlug, versionId);
104
+ const indexFile = await findMdxFile(fullPath, []);
105
+ if (indexFile) {
106
+ const frontmatter = await extractFrontmatterCached(indexFile, entryName);
107
+ if (!frontmatter.sidebarHidden) nodes.push({
108
+ slug: {
109
+ segments: childSlug,
110
+ versionId
111
+ },
112
+ path: `/${childSlug.join("/")}`,
113
+ title: frontmatter.title,
114
+ group: frontmatter.group,
115
+ icon: frontmatter.icon,
116
+ order: frontmatter.order ?? 999,
117
+ children,
118
+ isIndex: true,
119
+ versionId
120
+ });
121
+ } else if (children.length > 0) nodes.push({
122
+ slug: {
123
+ segments: childSlug,
124
+ versionId
125
+ },
126
+ path: `/${childSlug.join("/")}`,
127
+ title: formatTitle(entryName),
128
+ order: 999,
129
+ children,
130
+ isIndex: false,
131
+ versionId
132
+ });
133
+ } else if (entry.isFile() && /\.(mdx?|md)$/.test(entryName)) {
134
+ const baseName = entryName.replace(/\.(mdx?|md)$/, "");
135
+ if (baseName === "index") continue;
136
+ const frontmatter = await extractFrontmatterCached(fullPath, baseName);
137
+ if (!frontmatter.sidebarHidden) {
138
+ const childSlug = [...parentSlug, baseName];
139
+ nodes.push({
140
+ slug: {
141
+ segments: childSlug,
142
+ versionId
143
+ },
144
+ path: `/${childSlug.join("/")}`,
145
+ title: frontmatter.title,
146
+ group: frontmatter.group,
147
+ icon: frontmatter.icon,
148
+ order: frontmatter.order ?? 999,
149
+ children: [],
150
+ isIndex: false,
151
+ versionId
152
+ });
153
+ }
154
+ }
155
+ }
156
+ return nodes.sort((a, b) => a.order - b.order);
157
+ }
158
+ function parseFrontmatter$1(data, fallbackTitle) {
159
+ const result = docFrontmatterSchema.safeParse({
160
+ title: fallbackTitle,
161
+ ...data
162
+ });
163
+ if (result.success) return result.data;
164
+ return { title: formatTitle(fallbackTitle) };
165
+ }
166
+ function formatTitle(slug) {
167
+ return slug.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
168
+ }
169
+ async function extractFrontmatterCached(filePath, fallbackTitle) {
170
+ const mtime = (await fs.stat(filePath)).mtimeMs;
171
+ const cached = frontmatterCache.get(filePath);
172
+ if (cached && cached.mtime === mtime) return cached.frontmatter;
173
+ const handle = await fs.open(filePath, "r");
174
+ try {
175
+ const buffer = Buffer.alloc(FRONTMATTER_HEAD_BYTES);
176
+ const { bytesRead } = await handle.read(buffer, 0, FRONTMATTER_HEAD_BYTES, 0);
177
+ const head = buffer.subarray(0, bytesRead).toString("utf-8");
178
+ if (!head.startsWith("---")) {
179
+ const frontmatter$1 = { title: formatTitle(fallbackTitle) };
180
+ frontmatterCache.set(filePath, {
181
+ mtime,
182
+ frontmatter: frontmatter$1
183
+ });
184
+ return frontmatter$1;
185
+ }
186
+ if (!head.slice(3).match(/\r?\n---/)) {
187
+ const { data: data$1 } = matter(await fs.readFile(filePath, "utf-8"));
188
+ const frontmatter$1 = parseFrontmatter$1(data$1, fallbackTitle);
189
+ frontmatterCache.set(filePath, {
190
+ mtime,
191
+ frontmatter: frontmatter$1
192
+ });
193
+ return frontmatter$1;
194
+ }
195
+ const { data } = matter(head);
196
+ const frontmatter = parseFrontmatter$1(data, fallbackTitle);
197
+ frontmatterCache.set(filePath, {
198
+ mtime,
199
+ frontmatter
200
+ });
201
+ return frontmatter;
202
+ } finally {
203
+ await handle.close();
204
+ }
205
+ }
206
+ return {
207
+ async getTree(versionId) {
208
+ return buildTree(getDocsPath(versionId), [], versionId);
209
+ },
210
+ async getPage(_slug) {
211
+ return null;
212
+ },
213
+ async getPageRaw(slug) {
214
+ validateSlug(slug.segments);
215
+ const filePath = await findMdxFile(getDocsPath(slug.versionId), slug.segments);
216
+ if (!filePath) return null;
217
+ return {
218
+ content: await fs.readFile(filePath, "utf-8"),
219
+ filePath
220
+ };
221
+ }
222
+ };
223
+ }
224
+
225
+ //#endregion
226
+ //#region src/server/adapters/cached-docs.repository.ts
227
+ /**
228
+ * Caching decorator for DocsRepository with L1 (in-memory LRU) + L2 (CacheStore) layers.
229
+ *
230
+ * L1: In-process LRU cache for hot reads (60s TTL, 1s in dev)
231
+ * L2: Shared CacheStore (e.g., Redis) for cross-instance caching
232
+ */
233
+ function makeCachedDocsRepository(options) {
234
+ const { inner, cacheStore, logger, ttlSeconds, devMode } = options;
235
+ const lruCache = /* @__PURE__ */ new Map();
236
+ const LRU_TTL_MS = devMode ? 1e3 : 6e4;
237
+ const L2_TTL_MS = devMode ? 1e4 : ttlSeconds * 1e3;
238
+ const MAX_LRU_SIZE = 100;
239
+ function getFromLru(key) {
240
+ const entry = lruCache.get(key);
241
+ if (entry && entry.expires > Date.now()) {
242
+ lruCache.delete(key);
243
+ lruCache.set(key, entry);
244
+ logger.debug("Docs L1 cache hit", { key });
245
+ return entry.data;
246
+ }
247
+ lruCache.delete(key);
248
+ }
249
+ function setLru(key, data) {
250
+ if (lruCache.size >= MAX_LRU_SIZE) {
251
+ const firstKey = lruCache.keys().next().value;
252
+ if (firstKey) lruCache.delete(firstKey);
253
+ }
254
+ lruCache.set(key, {
255
+ data,
256
+ expires: Date.now() + LRU_TTL_MS
257
+ });
258
+ }
259
+ return {
260
+ async getTree(versionId) {
261
+ const key = `docs:tree:${versionId ?? "default"}`;
262
+ const lruHit = getFromLru(key);
263
+ if (lruHit) return lruHit;
264
+ const cached = await cacheStore.get(key);
265
+ if (cached) {
266
+ logger.debug("Docs L2 cache hit", { key });
267
+ setLru(key, cached);
268
+ return cached;
269
+ }
270
+ logger.debug("Docs cache miss, fetching tree", { key });
271
+ const tree = await inner.getTree(versionId);
272
+ await cacheStore.set(key, tree, L2_TTL_MS);
273
+ setLru(key, tree);
274
+ return tree;
275
+ },
276
+ async getPage(slug) {
277
+ const slugPath = slug.segments.join("/");
278
+ const key = `docs:page:${slug.versionId ?? "default"}:${slugPath}`;
279
+ const lruHit = getFromLru(key);
280
+ if (lruHit) return lruHit;
281
+ const cached = await cacheStore.get(key);
282
+ if (cached) {
283
+ logger.debug("Docs L2 cache hit", { key });
284
+ setLru(key, cached);
285
+ return cached;
286
+ }
287
+ const page = await inner.getPage(slug);
288
+ if (page) {
289
+ await cacheStore.set(key, page, L2_TTL_MS);
290
+ setLru(key, page);
291
+ }
292
+ return page;
293
+ },
294
+ getPageRaw: (slug) => inner.getPageRaw(slug)
295
+ };
296
+ }
297
+
298
+ //#endregion
299
+ //#region src/server/adapters/fumadocs-compiler.ts
300
+ const compiler = createCompiler({ preset: "fumadocs" });
301
+ function makeFumadocsCompiler() {
302
+ return { async compile(content, filePath) {
303
+ const { frontmatter, content: mdxContent } = parseFrontmatter(content);
304
+ const result = await compiler.compile({
305
+ source: mdxContent,
306
+ filePath,
307
+ skipRender: true
308
+ });
309
+ const toc = result.toc.map((item) => ({
310
+ id: item.url.replace(/^#/, ""),
311
+ text: typeof item.title === "string" ? item.title : String(item.title),
312
+ depth: item.depth
313
+ }));
314
+ return {
315
+ compiled: {
316
+ kind: "mdx-serialized",
317
+ code: result.compiled,
318
+ scope: {}
319
+ },
320
+ toc,
321
+ frontmatter
322
+ };
323
+ } };
324
+ }
325
+
326
+ //#endregion
327
+ //#region src/server/adapters/inmemory-search.adapter.ts
328
+ function makeInMemorySearch() {
329
+ let entries = [];
330
+ return {
331
+ async buildIndex(pages) {
332
+ entries = pages.map((page) => ({
333
+ id: page.slug.segments.join("/"),
334
+ path: page.path,
335
+ title: page.frontmatter.title,
336
+ snippet: page.frontmatter.description || "",
337
+ headings: page.toc.map((t) => t.text),
338
+ tags: page.frontmatter.tags || [],
339
+ versionId: page.versionId
340
+ }));
341
+ },
342
+ async search(query, versionId, limit = 10) {
343
+ const lowerQuery = query.toLowerCase();
344
+ const results = [];
345
+ for (const entry of entries) {
346
+ if (versionId && entry.versionId !== versionId) continue;
347
+ const titleMatch = entry.title.toLowerCase().includes(lowerQuery);
348
+ const snippetMatch = entry.snippet.toLowerCase().includes(lowerQuery);
349
+ const headingMatch = entry.headings.some((h) => h.toLowerCase().includes(lowerQuery));
350
+ const tagMatch = entry.tags.some((t) => t.toLowerCase().includes(lowerQuery));
351
+ if (titleMatch || snippetMatch || headingMatch || tagMatch) {
352
+ const score = titleMatch ? 10 : headingMatch ? 5 : tagMatch ? 3 : 1;
353
+ results.push({
354
+ entry,
355
+ score
356
+ });
357
+ }
358
+ if (results.length >= limit) break;
359
+ }
360
+ return results.sort((a, b) => b.score - a.score);
361
+ }
362
+ };
363
+ }
364
+
365
+ //#endregion
366
+ //#region src/server/usecases/get-doc-tree.ts
367
+ const makeGetDocTree = (deps) => {
368
+ return async (input) => {
369
+ return deps.docsRepository.getTree(input.versionId);
370
+ };
371
+ };
372
+
373
+ //#endregion
374
+ //#region src/server/usecases/get-doc-page.ts
375
+ const makeGetDocPage = (deps) => {
376
+ return async (input) => {
377
+ const { docsRepository, docsCompiler } = deps;
378
+ const docSlug = {
379
+ segments: input.slug,
380
+ versionId: input.versionId
381
+ };
382
+ const cached = await docsRepository.getPage(docSlug);
383
+ if (cached) return cached;
384
+ const raw = await docsRepository.getPageRaw(docSlug);
385
+ if (!raw) return null;
386
+ const result = await docsCompiler.compile(raw.content, raw.filePath);
387
+ const parsedFrontmatter = docFrontmatterSchema.parse(result.frontmatter);
388
+ return {
389
+ slug: docSlug,
390
+ path: raw.filePath,
391
+ frontmatter: parsedFrontmatter,
392
+ toc: result.toc,
393
+ compiled: result.compiled,
394
+ versionId: input.versionId
395
+ };
396
+ };
397
+ };
398
+
399
+ //#endregion
400
+ //#region src/server/usecases/search-docs.ts
401
+ const makeSearchDocs = (deps) => {
402
+ return async (input) => {
403
+ return deps.docsSearch.search(input.query, input.versionId, input.limit);
404
+ };
405
+ };
406
+
407
+ //#endregion
408
+ //#region src/server/router/docs.router.ts
409
+ const getTreeInputSchema = z.object({ versionId: z.string().optional() });
410
+ const getPageInputSchema = z.object({
411
+ slug: z.array(z.string()),
412
+ versionId: z.string().optional()
413
+ });
414
+ const searchInputSchema = z.object({
415
+ query: z.string(),
416
+ versionId: z.string().optional(),
417
+ limit: z.number().optional()
418
+ });
419
+ const docsRouter = {
420
+ getTree: publicProcedure.input(getTreeInputSchema).handler(async ({ input, context }) => {
421
+ const { getDocTree } = context.di.cradle;
422
+ return getDocTree({ versionId: input.versionId });
423
+ }),
424
+ getPage: publicProcedure.input(getPageInputSchema).handler(async ({ input, context }) => {
425
+ const { getDocPage } = context.di.cradle;
426
+ return getDocPage({
427
+ slug: input.slug,
428
+ versionId: input.versionId
429
+ });
430
+ }),
431
+ search: publicProcedure.input(searchInputSchema).handler(async ({ input, context }) => {
432
+ const { searchDocs } = context.di.cradle;
433
+ return searchDocs({
434
+ query: input.query,
435
+ versionId: input.versionId,
436
+ limit: input.limit
437
+ });
438
+ })
439
+ };
440
+
441
+ //#endregion
442
+ //#region src/server/module.ts
443
+ function collectSlugsFromTree(nodes) {
444
+ const result = [];
445
+ for (const node of nodes) {
446
+ result.push(node);
447
+ if (node.children.length > 0) result.push(...collectSlugsFromTree(node.children));
448
+ }
449
+ return result;
450
+ }
451
+ const kuckitModule = defineKuckitModule({
452
+ id: "kuckit.docs",
453
+ displayName: "Documentation",
454
+ description: "Markdown documentation with Fumadocs UI",
455
+ version: "0.1.0",
456
+ async register(ctx) {
457
+ const { container, config } = ctx;
458
+ const resolvedConfig = docsModuleConfigSchema.parse(config ?? {});
459
+ const filesystemDocsRepository = makeFilesystemDocsRepository({
460
+ docsDir: path.isAbsolute(resolvedConfig.docsDir) ? resolvedConfig.docsDir : path.resolve(process.cwd(), resolvedConfig.docsDir),
461
+ versions: resolvedConfig.versions
462
+ });
463
+ container.register({
464
+ docsCompiler: asFunction(() => makeFumadocsCompiler()).singleton(),
465
+ docsRepository: asFunction(({ cacheStore, logger }) => makeCachedDocsRepository({
466
+ inner: filesystemDocsRepository,
467
+ cacheStore,
468
+ logger,
469
+ ttlSeconds: resolvedConfig.cacheTtlSeconds,
470
+ devMode: resolvedConfig.devMode
471
+ })).singleton(),
472
+ docsSearch: asFunction(() => makeInMemorySearch()).singleton()
473
+ });
474
+ container.register({
475
+ getDocTree: asFunction(({ docsRepository }) => makeGetDocTree({ docsRepository })).scoped(),
476
+ getDocPage: asFunction(({ docsRepository, docsCompiler }) => makeGetDocPage({
477
+ docsRepository,
478
+ docsCompiler
479
+ })).scoped(),
480
+ searchDocs: asFunction(({ docsSearch }) => makeSearchDocs({ docsSearch })).scoped()
481
+ });
482
+ },
483
+ registerApi(ctx) {
484
+ ctx.addApiRegistration({
485
+ type: "rpc-router",
486
+ name: "docs",
487
+ router: docsRouter
488
+ });
489
+ },
490
+ async onBootstrap(ctx) {
491
+ const { container, config } = ctx;
492
+ const logger = container.resolve("logger");
493
+ const resolvedConfig = docsModuleConfigSchema.parse(config ?? {});
494
+ const absoluteDocsDir = path.isAbsolute(resolvedConfig.docsDir) ? resolvedConfig.docsDir : path.resolve(process.cwd(), resolvedConfig.docsDir);
495
+ logger.info(`Documentation module initialized. Docs dir: ${absoluteDocsDir}, cwd: ${process.cwd()}`);
496
+ setImmediate(async () => {
497
+ try {
498
+ const getDocTree = container.resolve("getDocTree");
499
+ const getDocPage = container.resolve("getDocPage");
500
+ const docsSearch = container.resolve("docsSearch");
501
+ const nodes = collectSlugsFromTree(await getDocTree());
502
+ const pages = [];
503
+ for (const node of nodes) {
504
+ const page = await getDocPage({
505
+ slug: node.slug.segments,
506
+ versionId: node.versionId
507
+ });
508
+ if (page && !page.frontmatter.searchHidden) pages.push(page);
509
+ }
510
+ await docsSearch.buildIndex(pages);
511
+ logger.info(`Search index built with ${pages.length} pages`);
512
+ } catch (error) {
513
+ logger.error("Failed to build search index", { error });
514
+ }
515
+ });
516
+ },
517
+ async onShutdown(ctx) {
518
+ const { container } = ctx;
519
+ container.resolve("logger").info("Documentation module shutting down");
520
+ }
521
+ });
522
+
523
+ //#endregion
524
+ export { kuckitModule };
525
+ //# sourceMappingURL=module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.js","names":["nodes: DocTreeNode[]","entries: Dirent[]","parseFrontmatter","frontmatter","data","toc: DocTocItem[]","entries: DocSearchEntry[]","results: DocSearchResult[]","docSlug: DocSlug","result: DocTreeNode[]","pages: DocPage[]"],"sources":["../../src/server/config.ts","../../src/server/domain/doc-node.ts","../../src/server/adapters/filesystem-docs.repository.ts","../../src/server/adapters/cached-docs.repository.ts","../../src/server/adapters/fumadocs-compiler.ts","../../src/server/adapters/inmemory-search.adapter.ts","../../src/server/usecases/get-doc-tree.ts","../../src/server/usecases/get-doc-page.ts","../../src/server/usecases/search-docs.ts","../../src/server/router/docs.router.ts","../../src/server/module.ts"],"sourcesContent":["import { z } from 'zod'\n\nexport const docsVersionConfigSchema = z.object({\n\tid: z.string(),\n\tlabel: z.string(),\n\tpathSegment: z.string(),\n\tdefault: z.boolean().optional(),\n\thidden: z.boolean().optional(),\n})\n\nexport const docsModuleConfigSchema = z.object({\n\tdocsDir: z.string().default('content/docs'),\n\tbasePath: z.string().default('/docs'),\n\tenableSearch: z.boolean().default(true),\n\tcacheTtlSeconds: z.number().default(300),\n\tdevMode: z.boolean().optional(),\n\tversions: z.array(docsVersionConfigSchema).optional(),\n\tfumadocs: z\n\t\t.object({\n\t\t\ttitle: z.string().optional(),\n\t\t\tdescription: z.string().optional(),\n\t\t\tlogoSrc: z.string().optional(),\n\t\t\tprimaryColor: z.string().optional(),\n\t\t\ttocDepth: z.number().default(3),\n\t\t})\n\t\t.optional(),\n})\n\nexport type DocsVersionConfig = z.infer<typeof docsVersionConfigSchema>\nexport type DocsModuleConfig = z.infer<typeof docsModuleConfigSchema>\n","import { z } from 'zod'\n\nexport const docSlugSchema = z.object({\n\tsegments: z.array(z.string()),\n\tversionId: z.string().optional(),\n})\n\nexport type DocSlug = z.infer<typeof docSlugSchema>\n\nexport interface DocFrontmatter {\n\treadonly title: string\n\treadonly description?: string\n\treadonly order?: number\n\treadonly group?: string\n\treadonly icon?: string\n\treadonly tags?: readonly string[]\n\treadonly sidebarHidden?: boolean\n\treadonly searchHidden?: boolean\n}\n\nexport const docFrontmatterSchema = z.object({\n\ttitle: z.string(),\n\tdescription: z.string().optional(),\n\torder: z.number().optional(),\n\tgroup: z.string().optional(),\n\ticon: z.string().optional(),\n\tsidebarHidden: z.boolean().optional(),\n\tsearchHidden: z.boolean().optional(),\n\ttags: z.array(z.string()).optional(),\n})\n\nexport interface DocTocItem {\n\treadonly id: string\n\treadonly depth: number\n\treadonly text: string\n}\n\nexport const docTocItemSchema = z.object({\n\tid: z.string(),\n\ttext: z.string(),\n\tdepth: z.number(),\n})\n\nexport interface DocTreeNode {\n\treadonly slug: DocSlug\n\treadonly path: string\n\treadonly title: string\n\treadonly group?: string\n\treadonly icon?: string\n\treadonly order: number\n\treadonly children: readonly DocTreeNode[]\n\treadonly isIndex: boolean\n\treadonly versionId?: string\n}\n\nexport interface CompiledMdxPayload {\n\treadonly kind: 'mdx-serialized'\n\treadonly code: string\n\treadonly scope: Record<string, unknown>\n}\n\nexport interface DocPage {\n\treadonly slug: DocSlug\n\treadonly path: string\n\treadonly frontmatter: DocFrontmatter\n\treadonly toc: readonly DocTocItem[]\n\treadonly compiled: CompiledMdxPayload\n\treadonly lastModified?: string\n\treadonly versionId?: string\n}\n","import * as fs from 'node:fs/promises'\nimport type { Dirent } from 'node:fs'\nimport * as path from 'node:path'\nimport matter from 'gray-matter'\nimport type { DocsRepository } from '../ports/docs-repository'\nimport type { DocTreeNode, DocPage, DocSlug, DocFrontmatter } from '../domain/doc-node'\nimport { docFrontmatterSchema } from '../domain/doc-node'\n\nexport interface FilesystemDocsRepositoryOptions {\n\tdocsDir: string\n\tversions?: { id: string; pathSegment: string }[]\n}\n\ninterface FrontmatterCacheEntry {\n\tmtime: number\n\tfrontmatter: DocFrontmatter\n}\n\nconst FRONTMATTER_HEAD_BYTES = 2048\n\nexport function makeFilesystemDocsRepository(\n\toptions: FilesystemDocsRepositoryOptions\n): DocsRepository {\n\tconst { docsDir, versions } = options\n\n\tconst frontmatterCache = new Map<string, FrontmatterCacheEntry>()\n\n\tfunction validateSlug(slug: string[]): void {\n\t\tif (slug.some((segment) => segment === '..' || segment === '' || segment.startsWith('/'))) {\n\t\t\tthrow new Error('Invalid slug: path traversal detected')\n\t\t}\n\t}\n\n\tfunction getDocsPath(versionId?: string): string {\n\t\tif (versionId && versions) {\n\t\t\tconst version = versions.find((v) => v.id === versionId)\n\t\t\tif (version) {\n\t\t\t\treturn path.join(docsDir, version.pathSegment)\n\t\t\t}\n\t\t}\n\t\treturn docsDir\n\t}\n\n\tasync function findMdxFile(basePath: string, slug: string[]): Promise<string | null> {\n\t\tconst slugPath = slug.join('/')\n\n\t\tconst candidates = [\n\t\t\tpath.join(basePath, `${slugPath}.mdx`),\n\t\t\tpath.join(basePath, `${slugPath}.md`),\n\t\t\tpath.join(basePath, slugPath, 'index.mdx'),\n\t\t\tpath.join(basePath, slugPath, 'index.md'),\n\t\t]\n\n\t\tif (slug.length === 0) {\n\t\t\tcandidates.unshift(path.join(basePath, 'index.mdx'), path.join(basePath, 'index.md'))\n\t\t}\n\n\t\tfor (const candidate of candidates) {\n\t\t\ttry {\n\t\t\t\tconst resolved = path.resolve(candidate)\n\t\t\t\tif (!resolved.startsWith(path.resolve(docsDir))) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tawait fs.access(resolved)\n\t\t\t\treturn resolved\n\t\t\t} catch {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\treturn null\n\t}\n\n\tasync function buildTree(\n\t\tdir: string,\n\t\tparentSlug: string[] = [],\n\t\tversionId?: string\n\t): Promise<DocTreeNode[]> {\n\t\tconst nodes: DocTreeNode[] = []\n\n\t\tlet entries: Dirent[]\n\t\ttry {\n\t\t\tentries = await fs.readdir(dir, { withFileTypes: true })\n\t\t} catch {\n\t\t\treturn []\n\t\t}\n\n\t\tfor (const entry of entries) {\n\t\t\tconst entryName = entry.name as string\n\t\t\tconst fullPath = path.join(dir, entryName)\n\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tconst childSlug = [...parentSlug, entryName]\n\t\t\t\tconst children = await buildTree(fullPath, childSlug, versionId)\n\n\t\t\t\tconst indexFile = await findMdxFile(fullPath, [])\n\t\t\t\tif (indexFile) {\n\t\t\t\t\tconst frontmatter = await extractFrontmatterCached(indexFile, entryName)\n\n\t\t\t\t\tif (!frontmatter.sidebarHidden) {\n\t\t\t\t\t\tnodes.push({\n\t\t\t\t\t\t\tslug: { segments: childSlug, versionId },\n\t\t\t\t\t\t\tpath: `/${childSlug.join('/')}`,\n\t\t\t\t\t\t\ttitle: frontmatter.title,\n\t\t\t\t\t\t\tgroup: frontmatter.group,\n\t\t\t\t\t\t\ticon: frontmatter.icon,\n\t\t\t\t\t\t\torder: frontmatter.order ?? 999,\n\t\t\t\t\t\t\tchildren,\n\t\t\t\t\t\t\tisIndex: true,\n\t\t\t\t\t\t\tversionId,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t} else if (children.length > 0) {\n\t\t\t\t\tnodes.push({\n\t\t\t\t\t\tslug: { segments: childSlug, versionId },\n\t\t\t\t\t\tpath: `/${childSlug.join('/')}`,\n\t\t\t\t\t\ttitle: formatTitle(entryName),\n\t\t\t\t\t\torder: 999,\n\t\t\t\t\t\tchildren,\n\t\t\t\t\t\tisIndex: false,\n\t\t\t\t\t\tversionId,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t} else if (entry.isFile() && /\\.(mdx?|md)$/.test(entryName)) {\n\t\t\t\tconst baseName = entryName.replace(/\\.(mdx?|md)$/, '')\n\t\t\t\tif (baseName === 'index') continue\n\n\t\t\t\tconst frontmatter = await extractFrontmatterCached(fullPath, baseName)\n\n\t\t\t\tif (!frontmatter.sidebarHidden) {\n\t\t\t\t\tconst childSlug = [...parentSlug, baseName]\n\t\t\t\t\tnodes.push({\n\t\t\t\t\t\tslug: { segments: childSlug, versionId },\n\t\t\t\t\t\tpath: `/${childSlug.join('/')}`,\n\t\t\t\t\t\ttitle: frontmatter.title,\n\t\t\t\t\t\tgroup: frontmatter.group,\n\t\t\t\t\t\ticon: frontmatter.icon,\n\t\t\t\t\t\torder: frontmatter.order ?? 999,\n\t\t\t\t\t\tchildren: [],\n\t\t\t\t\t\tisIndex: false,\n\t\t\t\t\t\tversionId,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn nodes.sort((a, b) => a.order - b.order)\n\t}\n\n\tfunction parseFrontmatter(data: Record<string, unknown>, fallbackTitle: string): DocFrontmatter {\n\t\tconst result = docFrontmatterSchema.safeParse({\n\t\t\ttitle: fallbackTitle,\n\t\t\t...data,\n\t\t})\n\t\tif (result.success) {\n\t\t\treturn result.data as DocFrontmatter\n\t\t}\n\t\treturn { title: formatTitle(fallbackTitle) }\n\t}\n\n\tfunction formatTitle(slug: string): string {\n\t\treturn slug.replace(/[-_]/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase())\n\t}\n\n\tasync function extractFrontmatterCached(\n\t\tfilePath: string,\n\t\tfallbackTitle: string\n\t): Promise<DocFrontmatter> {\n\t\tconst stat = await fs.stat(filePath)\n\t\tconst mtime = stat.mtimeMs\n\t\tconst cached = frontmatterCache.get(filePath)\n\n\t\tif (cached && cached.mtime === mtime) {\n\t\t\treturn cached.frontmatter\n\t\t}\n\n\t\tconst handle = await fs.open(filePath, 'r')\n\t\ttry {\n\t\t\tconst buffer = Buffer.alloc(FRONTMATTER_HEAD_BYTES)\n\t\t\tconst { bytesRead } = await handle.read(buffer, 0, FRONTMATTER_HEAD_BYTES, 0)\n\t\t\tconst head = buffer.subarray(0, bytesRead).toString('utf-8')\n\n\t\t\tif (!head.startsWith('---')) {\n\t\t\t\tconst frontmatter = { title: formatTitle(fallbackTitle) }\n\t\t\t\tfrontmatterCache.set(filePath, { mtime, frontmatter })\n\t\t\t\treturn frontmatter\n\t\t\t}\n\n\t\t\tconst endMatch = head.slice(3).match(/\\r?\\n---/)\n\t\t\tif (!endMatch) {\n\t\t\t\tconst fullContent = await fs.readFile(filePath, 'utf-8')\n\t\t\t\tconst { data } = matter(fullContent)\n\t\t\t\tconst frontmatter = parseFrontmatter(data, fallbackTitle)\n\t\t\t\tfrontmatterCache.set(filePath, { mtime, frontmatter })\n\t\t\t\treturn frontmatter\n\t\t\t}\n\n\t\t\tconst { data } = matter(head)\n\t\t\tconst frontmatter = parseFrontmatter(data, fallbackTitle)\n\t\t\tfrontmatterCache.set(filePath, { mtime, frontmatter })\n\t\t\treturn frontmatter\n\t\t} finally {\n\t\t\tawait handle.close()\n\t\t}\n\t}\n\n\treturn {\n\t\tasync getTree(versionId?: string): Promise<DocTreeNode[]> {\n\t\t\tconst basePath = getDocsPath(versionId)\n\t\t\treturn buildTree(basePath, [], versionId)\n\t\t},\n\n\t\tasync getPage(_slug: DocSlug): Promise<DocPage | null> {\n\t\t\treturn null\n\t\t},\n\n\t\tasync getPageRaw(slug: DocSlug): Promise<{ content: string; filePath: string } | null> {\n\t\t\tvalidateSlug(slug.segments)\n\t\t\tconst basePath = getDocsPath(slug.versionId)\n\t\t\tconst filePath = await findMdxFile(basePath, slug.segments)\n\n\t\t\tif (!filePath) {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\tconst content = await fs.readFile(filePath, 'utf-8')\n\t\t\treturn { content, filePath }\n\t\t},\n\t}\n}\n","import type { CacheStore, Logger } from '@kuckit/domain'\nimport type { DocsRepository } from '../ports/docs-repository'\nimport type { DocTreeNode, DocPage, DocSlug } from '../domain/doc-node'\n\nexport interface CachedDocsRepositoryOptions {\n\treadonly inner: DocsRepository\n\treadonly cacheStore: CacheStore\n\treadonly logger: Logger\n\treadonly ttlSeconds: number\n\treadonly devMode?: boolean\n}\n\ninterface LruEntry<T> {\n\tdata: T\n\texpires: number\n}\n\n/**\n * Caching decorator for DocsRepository with L1 (in-memory LRU) + L2 (CacheStore) layers.\n *\n * L1: In-process LRU cache for hot reads (60s TTL, 1s in dev)\n * L2: Shared CacheStore (e.g., Redis) for cross-instance caching\n */\nexport function makeCachedDocsRepository(options: CachedDocsRepositoryOptions): DocsRepository {\n\tconst { inner, cacheStore, logger, ttlSeconds, devMode } = options\n\n\tconst lruCache = new Map<string, LruEntry<unknown>>()\n\tconst LRU_TTL_MS = devMode ? 1_000 : 60_000\n\tconst L2_TTL_MS = devMode ? 10_000 : ttlSeconds * 1000\n\tconst MAX_LRU_SIZE = 100\n\n\tfunction getFromLru<T>(key: string): T | undefined {\n\t\tconst entry = lruCache.get(key)\n\t\tif (entry && entry.expires > Date.now()) {\n\t\t\t// Move to end of iteration order (LRU behavior)\n\t\t\tlruCache.delete(key)\n\t\t\tlruCache.set(key, entry)\n\t\t\tlogger.debug('Docs L1 cache hit', { key })\n\t\t\treturn entry.data as T\n\t\t}\n\t\tlruCache.delete(key)\n\t\treturn undefined\n\t}\n\n\tfunction setLru<T>(key: string, data: T): void {\n\t\tif (lruCache.size >= MAX_LRU_SIZE) {\n\t\t\tconst firstKey = lruCache.keys().next().value\n\t\t\tif (firstKey) lruCache.delete(firstKey)\n\t\t}\n\t\tlruCache.set(key, { data, expires: Date.now() + LRU_TTL_MS })\n\t}\n\n\treturn {\n\t\tasync getTree(versionId?: string): Promise<DocTreeNode[]> {\n\t\t\tconst key = `docs:tree:${versionId ?? 'default'}`\n\n\t\t\tconst lruHit = getFromLru<DocTreeNode[]>(key)\n\t\t\tif (lruHit) return lruHit\n\n\t\t\tconst cached = await cacheStore.get<DocTreeNode[]>(key)\n\t\t\tif (cached) {\n\t\t\t\tlogger.debug('Docs L2 cache hit', { key })\n\t\t\t\tsetLru(key, cached)\n\t\t\t\treturn cached\n\t\t\t}\n\n\t\t\tlogger.debug('Docs cache miss, fetching tree', { key })\n\t\t\tconst tree = await inner.getTree(versionId)\n\t\t\tawait cacheStore.set(key, tree, L2_TTL_MS)\n\t\t\tsetLru(key, tree)\n\t\t\treturn tree\n\t\t},\n\n\t\tasync getPage(slug: DocSlug): Promise<DocPage | null> {\n\t\t\tconst slugPath = slug.segments.join('/')\n\t\t\tconst key = `docs:page:${slug.versionId ?? 'default'}:${slugPath}`\n\n\t\t\tconst lruHit = getFromLru<DocPage>(key)\n\t\t\tif (lruHit) return lruHit\n\n\t\t\tconst cached = await cacheStore.get<DocPage>(key)\n\t\t\tif (cached) {\n\t\t\t\tlogger.debug('Docs L2 cache hit', { key })\n\t\t\t\tsetLru(key, cached)\n\t\t\t\treturn cached\n\t\t\t}\n\n\t\t\tconst page = await inner.getPage(slug)\n\t\t\tif (page) {\n\t\t\t\tawait cacheStore.set(key, page, L2_TTL_MS)\n\t\t\t\tsetLru(key, page)\n\t\t\t}\n\t\t\treturn page\n\t\t},\n\n\t\tgetPageRaw: (slug) => inner.getPageRaw(slug),\n\t}\n}\n","import { createCompiler, parseFrontmatter } from '@fumadocs/mdx-remote'\nimport type { DocsCompiler, CompileResult } from '../ports/docs-compiler'\nimport type { DocTocItem } from '../domain/doc-node'\n\nconst compiler = createCompiler({\n\tpreset: 'fumadocs',\n})\n\nexport function makeFumadocsCompiler(): DocsCompiler {\n\treturn {\n\t\tasync compile(content: string, filePath: string): Promise<CompileResult> {\n\t\t\tconst { frontmatter, content: mdxContent } = parseFrontmatter(content)\n\n\t\t\tconst result = await compiler.compile({\n\t\t\t\tsource: mdxContent,\n\t\t\t\tfilePath,\n\t\t\t\tskipRender: true,\n\t\t\t})\n\n\t\t\tconst toc: DocTocItem[] = result.toc.map((item) => ({\n\t\t\t\tid: item.url.replace(/^#/, ''),\n\t\t\t\ttext: typeof item.title === 'string' ? item.title : String(item.title),\n\t\t\t\tdepth: item.depth,\n\t\t\t}))\n\n\t\t\treturn {\n\t\t\t\tcompiled: {\n\t\t\t\t\tkind: 'mdx-serialized',\n\t\t\t\t\tcode: result.compiled,\n\t\t\t\t\tscope: {},\n\t\t\t\t},\n\t\t\t\ttoc,\n\t\t\t\tfrontmatter,\n\t\t\t}\n\t\t},\n\t}\n}\n","import type { DocsSearch } from '../ports/docs-search'\nimport type { DocPage } from '../domain/doc-node'\nimport type { DocSearchEntry, DocSearchResult } from '../domain/search-entry'\n\nexport function makeInMemorySearch(): DocsSearch {\n\tlet entries: DocSearchEntry[] = []\n\n\treturn {\n\t\tasync buildIndex(pages: DocPage[]): Promise<void> {\n\t\t\tentries = pages.map((page) => ({\n\t\t\t\tid: page.slug.segments.join('/'),\n\t\t\t\tpath: page.path,\n\t\t\t\ttitle: page.frontmatter.title,\n\t\t\t\tsnippet: page.frontmatter.description || '',\n\t\t\t\theadings: page.toc.map((t) => t.text),\n\t\t\t\ttags: page.frontmatter.tags || [],\n\t\t\t\tversionId: page.versionId,\n\t\t\t}))\n\t\t},\n\n\t\tasync search(query: string, versionId?: string, limit = 10): Promise<DocSearchResult[]> {\n\t\t\tconst lowerQuery = query.toLowerCase()\n\t\t\tconst results: DocSearchResult[] = []\n\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (versionId && entry.versionId !== versionId) continue\n\n\t\t\t\tconst titleMatch = entry.title.toLowerCase().includes(lowerQuery)\n\t\t\t\tconst snippetMatch = entry.snippet.toLowerCase().includes(lowerQuery)\n\t\t\t\tconst headingMatch = entry.headings.some((h) => h.toLowerCase().includes(lowerQuery))\n\t\t\t\tconst tagMatch = entry.tags.some((t) => t.toLowerCase().includes(lowerQuery))\n\n\t\t\t\tif (titleMatch || snippetMatch || headingMatch || tagMatch) {\n\t\t\t\t\tconst score = titleMatch ? 10 : headingMatch ? 5 : tagMatch ? 3 : 1\n\t\t\t\t\tresults.push({ entry, score })\n\t\t\t\t}\n\n\t\t\t\tif (results.length >= limit) break\n\t\t\t}\n\n\t\t\treturn results.sort((a, b) => b.score - a.score)\n\t\t},\n\t}\n}\n","import type { DocsRepository } from '../ports/docs-repository'\nimport type { DocTreeNode } from '../domain/doc-node'\n\nexport interface GetDocTreeDeps {\n\treadonly docsRepository: DocsRepository\n}\n\nexport interface GetDocTreeInput {\n\treadonly versionId?: string\n}\n\nexport type GetDocTreeOutput = DocTreeNode[]\n\nexport const makeGetDocTree = (deps: GetDocTreeDeps) => {\n\treturn async (input: GetDocTreeInput): Promise<GetDocTreeOutput> => {\n\t\treturn deps.docsRepository.getTree(input.versionId)\n\t}\n}\n","import type { DocsRepository } from '../ports/docs-repository'\nimport type { DocsCompiler } from '../ports/docs-compiler'\nimport type { DocPage, DocSlug, DocFrontmatter } from '../domain/doc-node'\nimport { docFrontmatterSchema } from '../domain/doc-node'\n\nexport interface GetDocPageDeps {\n\treadonly docsRepository: DocsRepository\n\treadonly docsCompiler: DocsCompiler\n}\n\nexport interface GetDocPageInput {\n\treadonly slug: string[]\n\treadonly versionId?: string\n}\n\nexport type GetDocPageOutput = DocPage | null\n\nexport const makeGetDocPage = (deps: GetDocPageDeps) => {\n\treturn async (input: GetDocPageInput): Promise<GetDocPageOutput> => {\n\t\tconst { docsRepository, docsCompiler } = deps\n\n\t\tconst docSlug: DocSlug = {\n\t\t\tsegments: input.slug,\n\t\t\tversionId: input.versionId,\n\t\t}\n\n\t\tconst cached = await docsRepository.getPage(docSlug)\n\t\tif (cached) {\n\t\t\treturn cached\n\t\t}\n\n\t\tconst raw = await docsRepository.getPageRaw(docSlug)\n\t\tif (!raw) {\n\t\t\treturn null\n\t\t}\n\n\t\tconst result = await docsCompiler.compile(raw.content, raw.filePath)\n\t\tconst parsedFrontmatter = docFrontmatterSchema.parse(result.frontmatter)\n\n\t\treturn {\n\t\t\tslug: docSlug,\n\t\t\tpath: raw.filePath,\n\t\t\tfrontmatter: parsedFrontmatter as DocFrontmatter,\n\t\t\ttoc: result.toc,\n\t\t\tcompiled: result.compiled,\n\t\t\tversionId: input.versionId,\n\t\t}\n\t}\n}\n","import type { DocsSearch } from '../ports/docs-search'\nimport type { DocSearchResult } from '../domain/search-entry'\n\nexport interface SearchDocsDeps {\n\treadonly docsSearch: DocsSearch\n}\n\nexport interface SearchDocsInput {\n\treadonly query: string\n\treadonly versionId?: string\n\treadonly limit?: number\n}\n\nexport type SearchDocsOutput = DocSearchResult[]\n\nexport const makeSearchDocs = (deps: SearchDocsDeps) => {\n\treturn async (input: SearchDocsInput): Promise<SearchDocsOutput> => {\n\t\treturn deps.docsSearch.search(input.query, input.versionId, input.limit)\n\t}\n}\n","import { publicProcedure } from '@kuckit/api'\nimport { z } from 'zod'\nimport type { GetDocTreeInput, GetDocTreeOutput } from '../usecases/get-doc-tree'\nimport type { GetDocPageInput, GetDocPageOutput } from '../usecases/get-doc-page'\nimport type { SearchDocsInput, SearchDocsOutput } from '../usecases/search-docs'\n\nconst getTreeInputSchema = z.object({\n\tversionId: z.string().optional(),\n})\n\nconst getPageInputSchema = z.object({\n\tslug: z.array(z.string()),\n\tversionId: z.string().optional(),\n})\n\nconst searchInputSchema = z.object({\n\tquery: z.string(),\n\tversionId: z.string().optional(),\n\tlimit: z.number().optional(),\n})\n\ninterface DocsCradle {\n\tgetDocTree: (input: GetDocTreeInput) => Promise<GetDocTreeOutput>\n\tgetDocPage: (input: GetDocPageInput) => Promise<GetDocPageOutput>\n\tsearchDocs: (input: SearchDocsInput) => Promise<SearchDocsOutput>\n}\n\nexport const docsRouter = {\n\tgetTree: publicProcedure.input(getTreeInputSchema).handler(async ({ input, context }) => {\n\t\tconst { getDocTree } = context.di.cradle as DocsCradle\n\t\treturn getDocTree({ versionId: input.versionId })\n\t}),\n\n\tgetPage: publicProcedure.input(getPageInputSchema).handler(async ({ input, context }) => {\n\t\tconst { getDocPage } = context.di.cradle as DocsCradle\n\t\treturn getDocPage({ slug: input.slug, versionId: input.versionId })\n\t}),\n\n\tsearch: publicProcedure.input(searchInputSchema).handler(async ({ input, context }) => {\n\t\tconst { searchDocs } = context.di.cradle as DocsCradle\n\t\treturn searchDocs({ query: input.query, versionId: input.versionId, limit: input.limit })\n\t}),\n}\n","import * as path from 'node:path'\nimport { defineKuckitModule, asFunction, type KuckitModuleContext } from '@kuckit/sdk'\nimport { docsModuleConfigSchema, type DocsModuleConfig } from './config'\nimport { makeFilesystemDocsRepository } from './adapters/filesystem-docs.repository'\nimport { makeCachedDocsRepository } from './adapters/cached-docs.repository'\nimport { makeFumadocsCompiler } from './adapters/fumadocs-compiler'\nimport { makeInMemorySearch } from './adapters/inmemory-search.adapter'\nimport { makeGetDocTree } from './usecases/get-doc-tree'\nimport { makeGetDocPage } from './usecases/get-doc-page'\nimport { makeSearchDocs } from './usecases/search-docs'\nimport { docsRouter } from './router/docs.router'\nimport type { DocTreeNode, DocPage } from './domain/doc-node'\n\nexport type { DocsModuleConfig }\n\nfunction collectSlugsFromTree(nodes: readonly DocTreeNode[]): DocTreeNode[] {\n\tconst result: DocTreeNode[] = []\n\tfor (const node of nodes) {\n\t\tresult.push(node)\n\t\tif (node.children.length > 0) {\n\t\t\tresult.push(...collectSlugsFromTree(node.children))\n\t\t}\n\t}\n\treturn result\n}\n\nexport const kuckitModule = defineKuckitModule<DocsModuleConfig>({\n\tid: 'kuckit.docs',\n\tdisplayName: 'Documentation',\n\tdescription: 'Markdown documentation with Fumadocs UI',\n\tversion: '0.1.0',\n\n\tasync register(ctx: KuckitModuleContext<DocsModuleConfig>) {\n\t\tconst { container, config } = ctx\n\t\tconst resolvedConfig = docsModuleConfigSchema.parse(config ?? {})\n\n\t\tconst absoluteDocsDir = path.isAbsolute(resolvedConfig.docsDir)\n\t\t\t? resolvedConfig.docsDir\n\t\t\t: path.resolve(process.cwd(), resolvedConfig.docsDir)\n\n\t\tconst filesystemDocsRepository = makeFilesystemDocsRepository({\n\t\t\tdocsDir: absoluteDocsDir,\n\t\t\tversions: resolvedConfig.versions,\n\t\t})\n\n\t\tcontainer.register({\n\t\t\tdocsCompiler: asFunction(() => makeFumadocsCompiler()).singleton(),\n\n\t\t\tdocsRepository: asFunction(({ cacheStore, logger }) =>\n\t\t\t\tmakeCachedDocsRepository({\n\t\t\t\t\tinner: filesystemDocsRepository,\n\t\t\t\t\tcacheStore,\n\t\t\t\t\tlogger,\n\t\t\t\t\tttlSeconds: resolvedConfig.cacheTtlSeconds,\n\t\t\t\t\tdevMode: resolvedConfig.devMode,\n\t\t\t\t})\n\t\t\t).singleton(),\n\n\t\t\tdocsSearch: asFunction(() => makeInMemorySearch()).singleton(),\n\t\t})\n\n\t\tcontainer.register({\n\t\t\tgetDocTree: asFunction(({ docsRepository }) => makeGetDocTree({ docsRepository })).scoped(),\n\n\t\t\tgetDocPage: asFunction(({ docsRepository, docsCompiler }) =>\n\t\t\t\tmakeGetDocPage({ docsRepository, docsCompiler })\n\t\t\t).scoped(),\n\n\t\t\tsearchDocs: asFunction(({ docsSearch }) => makeSearchDocs({ docsSearch })).scoped(),\n\t\t})\n\t},\n\n\tregisterApi(ctx) {\n\t\tctx.addApiRegistration({\n\t\t\ttype: 'rpc-router',\n\t\t\tname: 'docs',\n\t\t\trouter: docsRouter,\n\t\t})\n\t},\n\n\tasync onBootstrap(ctx: KuckitModuleContext<DocsModuleConfig>) {\n\t\tconst { container, config } = ctx\n\t\tconst logger = container.resolve('logger')\n\t\tconst resolvedConfig = docsModuleConfigSchema.parse(config ?? {})\n\t\tconst absoluteDocsDir = path.isAbsolute(resolvedConfig.docsDir)\n\t\t\t? resolvedConfig.docsDir\n\t\t\t: path.resolve(process.cwd(), resolvedConfig.docsDir)\n\t\tlogger.info(\n\t\t\t`Documentation module initialized. Docs dir: ${absoluteDocsDir}, cwd: ${process.cwd()}`\n\t\t)\n\n\t\t// Build search index in background to not block startup\n\t\tsetImmediate(async () => {\n\t\t\ttry {\n\t\t\t\tconst getDocTree = container.resolve('getDocTree')\n\t\t\t\tconst getDocPage = container.resolve('getDocPage')\n\t\t\t\tconst docsSearch = container.resolve('docsSearch')\n\n\t\t\t\tconst tree = await getDocTree()\n\t\t\t\tconst nodes = collectSlugsFromTree(tree)\n\n\t\t\t\tconst pages: DocPage[] = []\n\t\t\t\tfor (const node of nodes) {\n\t\t\t\t\tconst page = await getDocPage({ slug: node.slug.segments, versionId: node.versionId })\n\t\t\t\t\tif (page && !page.frontmatter.searchHidden) {\n\t\t\t\t\t\tpages.push(page)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tawait docsSearch.buildIndex(pages)\n\t\t\t\tlogger.info(`Search index built with ${pages.length} pages`)\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error('Failed to build search index', { error })\n\t\t\t}\n\t\t})\n\t},\n\n\tasync onShutdown(ctx: KuckitModuleContext<DocsModuleConfig>) {\n\t\tconst { container } = ctx\n\t\tconst logger = container.resolve('logger')\n\t\tlogger.info('Documentation module shutting down')\n\t},\n})\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@kuckit/docs-module",
3
+ "version": "0.1.1",
4
+ "description": "Documentation module for Kuckit using Fumadocs",
5
+ "type": "module",
6
+ "main": "dist/server/module.js",
7
+ "types": "dist/server/module.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/server/module.d.ts",
14
+ "default": "./dist/server/module.js"
15
+ },
16
+ "./client": {
17
+ "types": "./dist/client/index.d.ts",
18
+ "default": "./dist/client/index.js"
19
+ }
20
+ },
21
+ "kuckit": {
22
+ "id": "kuckit.docs",
23
+ "server": ".",
24
+ "client": "./client"
25
+ },
26
+ "scripts": {
27
+ "build": "tsdown",
28
+ "prepublishOnly": "npm run build && node ../../scripts/resolve-workspace-protocols.cjs && node ../../scripts/check-no-workspace-protocol.cjs"
29
+ },
30
+ "peerDependencies": {
31
+ "@kuckit/sdk": "^2.0.1",
32
+ "@kuckit/sdk-react": "^2.0.1",
33
+ "@tanstack/react-router": "^1",
34
+ "react": "^18 || ^19",
35
+ "typescript": "^5"
36
+ },
37
+ "dependencies": {
38
+ "@kuckit/api": "^2.0.1",
39
+ "@kuckit/domain": "^2.0.1",
40
+ "@fumadocs/mdx-remote": "^1.3.0",
41
+ "fumadocs-core": "^15.3.1",
42
+ "fumadocs-ui": "^15.3.1",
43
+ "gray-matter": "^4.0.3",
44
+ "lucide-react": "^0.379.0",
45
+ "zod": "^4.1.11"
46
+ },
47
+ "devDependencies": {
48
+ "@types/react": "^19.0.0",
49
+ "tsdown": "catalog:"
50
+ }
51
+ }