@kuckit/docs-module 4.0.5 → 5.0.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.
@@ -172,9 +172,9 @@ function makeFilesystemDocsRepository(options) {
172
172
  if (cached && cached.mtime === mtime) return cached.frontmatter;
173
173
  const handle = await fs.open(filePath, "r");
174
174
  try {
175
- const buffer = Buffer.alloc(FRONTMATTER_HEAD_BYTES);
175
+ const buffer = new Uint8Array(FRONTMATTER_HEAD_BYTES);
176
176
  const { bytesRead } = await handle.read(buffer, 0, FRONTMATTER_HEAD_BYTES, 0);
177
- const head = buffer.subarray(0, bytesRead).toString("utf-8");
177
+ const head = Buffer.from(buffer.subarray(0, bytesRead)).toString("utf-8");
178
178
  if (!head.startsWith("---")) {
179
179
  const frontmatter$1 = { title: formatTitle(fallbackTitle) };
180
180
  frontmatterCache.set(filePath, {
@@ -215,7 +215,7 @@ function makeFilesystemDocsRepository(options) {
215
215
  const filePath = await findMdxFile(getDocsPath(slug.versionId), slug.segments);
216
216
  if (!filePath) return null;
217
217
  return {
218
- content: await fs.readFile(filePath, "utf-8"),
218
+ content: (await fs.readFile(filePath, "utf-8")).toString(),
219
219
  filePath
220
220
  };
221
221
  }
@@ -1 +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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"}
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 = new Uint8Array(FRONTMATTER_HEAD_BYTES)\n\t\t\tconst { bytesRead } = await handle.read(buffer, 0, FRONTMATTER_HEAD_BYTES, 0)\n\t\t\tconst head = Buffer.from(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')).toString()\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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kuckit/docs-module",
3
- "version": "4.0.5",
3
+ "version": "5.0.1",
4
4
  "description": "Documentation module for Kuckit using Fumadocs",
5
5
  "type": "module",
6
6
  "main": "dist/server/module.js",
@@ -52,22 +52,23 @@
52
52
  "prepublishOnly": "npm run build && node ../../scripts/resolve-workspace-protocols.cjs && node ../../scripts/check-no-workspace-protocol.cjs"
53
53
  },
54
54
  "peerDependencies": {
55
- "@kuckit/sdk": "^4.0.5",
56
- "@kuckit/sdk-react": "^4.0.5",
55
+ "@kuckit/sdk": "^5.0.1",
56
+ "@kuckit/sdk-react": "^5.0.1",
57
57
  "@tailwindcss/typography": "^0.5",
58
58
  "@tanstack/react-router": "^1",
59
59
  "react": "^18 || ^19",
60
60
  "typescript": "^5"
61
61
  },
62
62
  "dependencies": {
63
- "@kuckit/api": "^4.0.5",
64
- "@kuckit/domain": "^4.0.5",
63
+ "@kuckit/api": "^5.0.1",
64
+ "@kuckit/domain": "^5.0.1",
65
65
  "@fumadocs/mdx-remote": "^1.3.0",
66
66
  "gray-matter": "^4.0.3",
67
67
  "lucide-react": "^0.379.0",
68
68
  "zod": "^4.1.11"
69
69
  },
70
70
  "devDependencies": {
71
+ "@playwright/test": "^1.52.0",
71
72
  "@types/react": "^19.0.0",
72
73
  "tsdown": "catalog:"
73
74
  }