specra 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.MD +21 -0
- package/README.md +157 -0
- package/dist/app/api/mdx-watch/route.d.mts +12 -0
- package/dist/app/api/mdx-watch/route.d.ts +12 -0
- package/dist/app/api/mdx-watch/route.js +98 -0
- package/dist/app/api/mdx-watch/route.js.map +1 -0
- package/dist/app/api/mdx-watch/route.mjs +71 -0
- package/dist/app/api/mdx-watch/route.mjs.map +1 -0
- package/dist/app/docs-page.d.mts +32 -0
- package/dist/app/docs-page.d.ts +32 -0
- package/dist/app/docs-page.js +4072 -0
- package/dist/app/docs-page.js.map +1 -0
- package/dist/app/docs-page.mjs +14 -0
- package/dist/app/docs-page.mjs.map +1 -0
- package/dist/app/layout.css +297 -0
- package/dist/app/layout.css.map +1 -0
- package/dist/app/layout.d.mts +19 -0
- package/dist/app/layout.d.ts +19 -0
- package/dist/app/layout.js +112 -0
- package/dist/app/layout.js.map +1 -0
- package/dist/app/layout.mjs +13 -0
- package/dist/app/layout.mjs.map +1 -0
- package/dist/chunk-DR4EPLMT.mjs +1013 -0
- package/dist/chunk-DR4EPLMT.mjs.map +1 -0
- package/dist/chunk-INL2EC72.mjs +170 -0
- package/dist/chunk-INL2EC72.mjs.map +1 -0
- package/dist/chunk-IZFGEAD6.mjs +61 -0
- package/dist/chunk-IZFGEAD6.mjs.map +1 -0
- package/dist/chunk-KTRWWAGL.mjs +50 -0
- package/dist/chunk-KTRWWAGL.mjs.map +1 -0
- package/dist/chunk-MZJHJ6BV.mjs +21 -0
- package/dist/chunk-MZJHJ6BV.mjs.map +1 -0
- package/dist/chunk-NXRIAL7T.mjs +3119 -0
- package/dist/chunk-NXRIAL7T.mjs.map +1 -0
- package/dist/components/index.d.mts +822 -0
- package/dist/components/index.d.ts +822 -0
- package/dist/components/index.js +3738 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/index.mjs +3627 -0
- package/dist/components/index.mjs.map +1 -0
- package/dist/index.css +297 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.mts +545 -0
- package/dist/index.d.ts +545 -0
- package/dist/index.js +4648 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +347 -0
- package/dist/index.mjs.map +1 -0
- package/dist/lib/index.d.mts +798 -0
- package/dist/lib/index.d.ts +798 -0
- package/dist/lib/index.js +1301 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/index.mjs +89 -0
- package/dist/lib/index.mjs.map +1 -0
- package/package.json +119 -0
- package/src/app/api/mdx-watch/route.ts +86 -0
- package/src/app/docs-page.tsx +212 -0
- package/src/app/layout.tsx +74 -0
- package/src/components/docs/accordion.tsx +53 -0
- package/src/components/docs/api/api-endpoint.tsx +59 -0
- package/src/components/docs/api/api-params.tsx +43 -0
- package/src/components/docs/api/api-playground.tsx +233 -0
- package/src/components/docs/api/api-reference.tsx +291 -0
- package/src/components/docs/api/api-response.tsx +48 -0
- package/src/components/docs/api/index.ts +5 -0
- package/src/components/docs/badge.tsx +22 -0
- package/src/components/docs/breadcrumb.tsx +51 -0
- package/src/components/docs/callout.tsx +109 -0
- package/src/components/docs/card.tsx +84 -0
- package/src/components/docs/category-index.tsx +112 -0
- package/src/components/docs/code-block.tsx +129 -0
- package/src/components/docs/columns.tsx +45 -0
- package/src/components/docs/componentTextProps.ts +85 -0
- package/src/components/docs/dev-mode-badge.tsx +35 -0
- package/src/components/docs/doc-layout-wrapper.tsx +54 -0
- package/src/components/docs/doc-layout.tsx +111 -0
- package/src/components/docs/doc-loading.tsx +15 -0
- package/src/components/docs/doc-metadata.tsx +55 -0
- package/src/components/docs/doc-navigation.tsx +62 -0
- package/src/components/docs/doc-tags.tsx +25 -0
- package/src/components/docs/draft-badge.tsx +10 -0
- package/src/components/docs/footer.tsx +47 -0
- package/src/components/docs/frame.tsx +22 -0
- package/src/components/docs/header.tsx +122 -0
- package/src/components/docs/hot-reload-indicator.tsx +77 -0
- package/src/components/docs/icon.tsx +70 -0
- package/src/components/docs/image-card.tsx +95 -0
- package/src/components/docs/image.tsx +73 -0
- package/src/components/docs/index.ts +48 -0
- package/src/components/docs/math.tsx +46 -0
- package/src/components/docs/mdx-components.tsx +166 -0
- package/src/components/docs/mdx-hot-reload.tsx +37 -0
- package/src/components/docs/mermaid.tsx +77 -0
- package/src/components/docs/mobile-doc-layout.tsx +115 -0
- package/src/components/docs/not-found-content.tsx +55 -0
- package/src/components/docs/search-highlight.tsx +127 -0
- package/src/components/docs/search-modal.tsx +223 -0
- package/src/components/docs/sidebar-skeleton.tsx +39 -0
- package/src/components/docs/sidebar.tsx +323 -0
- package/src/components/docs/site-banner.tsx +92 -0
- package/src/components/docs/steps.tsx +29 -0
- package/src/components/docs/tab-context.tsx +28 -0
- package/src/components/docs/tab-groups.tsx +50 -0
- package/src/components/docs/table-of-contents.tsx +104 -0
- package/src/components/docs/tabs.tsx +63 -0
- package/src/components/docs/theme-toggle.tsx +39 -0
- package/src/components/docs/tooltip.tsx +37 -0
- package/src/components/docs/version-switcher.tsx +52 -0
- package/src/components/docs/video.tsx +80 -0
- package/src/components/global/index.ts +3 -0
- package/src/components/global/version-not-found.tsx +26 -0
- package/src/components/index.ts +8 -0
- package/src/components/theme-provider.tsx +11 -0
- package/src/components/ui/badge.tsx +46 -0
- package/src/components/ui/button.tsx +60 -0
- package/src/components/ui/dialog.tsx +143 -0
- package/src/components/ui/index.ts +6 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/index.ts +41 -0
- package/src/lib/api-parser.types.ts +78 -0
- package/src/lib/api.types.ts +202 -0
- package/src/lib/category.ts +71 -0
- package/src/lib/config.server.ts +170 -0
- package/src/lib/config.ts +20 -0
- package/src/lib/config.types.ts +295 -0
- package/src/lib/dev-utils.ts +75 -0
- package/src/lib/index.ts +27 -0
- package/src/lib/mdx-cache.ts +200 -0
- package/src/lib/mdx.ts +402 -0
- package/src/lib/parsers/base-parser.ts +16 -0
- package/src/lib/parsers/index.ts +69 -0
- package/src/lib/parsers/openapi-parser.ts +251 -0
- package/src/lib/parsers/postman-parser.ts +301 -0
- package/src/lib/parsers/specra-parser.ts +24 -0
- package/src/lib/redirects.ts +40 -0
- package/src/lib/remark-code-meta.ts +23 -0
- package/src/lib/sidebar-utils.ts +188 -0
- package/src/lib/toc.ts +24 -0
- package/src/lib/utils.ts +36 -0
- package/src/specra.config.json +124 -0
- package/src/styles/globals.css +427 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/index.ts","../../src/lib/mdx.ts","../../src/lib/category.ts","../../src/lib/sidebar-utils.ts","../../src/lib/mdx-cache.ts","../../src/lib/dev-utils.ts","../../src/lib/toc.ts","../../src/lib/config.types.ts","../../src/lib/config.server.ts","../../src/lib/parsers/specra-parser.ts","../../src/lib/parsers/openapi-parser.ts","../../src/lib/parsers/postman-parser.ts","../../src/lib/parsers/index.ts","../../src/lib/utils.ts","../../src/lib/redirects.ts"],"sourcesContent":["// MDX Processing\nexport * from './mdx'\nexport * from './mdx-cache'\nexport * from './toc'\n\n// Configuration\nexport * from './config.server'\nexport * from './config'\nexport type * from './config.types'\n\n// API Parsers\nexport * from './parsers'\nexport type * from './api.types'\nexport type {\n ApiParam,\n ApiHeader,\n ApiResponse as SpecraApiResponse,\n ApiEndpointSpec,\n SpecraApiSpec\n} from './api-parser.types'\n\n// Utilities\nexport * from './utils'\nexport * from './sidebar-utils'\nexport * from './category'\nexport * from './redirects'\nexport * from './dev-utils'","import fs from \"fs\"\nimport path from \"path\"\nimport matter from \"gray-matter\"\nimport { getAllCategoryConfigs } from \"./category\"\nimport { sortSidebarItems, sortSidebarGroups, buildSidebarStructure, type SidebarGroup } from \"./sidebar-utils\"\n\nconst DOCS_DIR = path.join(process.cwd(), \"docs\")\n\n/**\n * Calculate reading time based on word count\n * Average reading speed: 200 words per minute\n */\nfunction calculateReadingTime(content: string): { minutes: number; words: number } {\n const words = content.trim().split(/\\s+/).length\n const minutes = Math.ceil(words / 200)\n return { minutes, words }\n}\n\nexport interface DocMeta {\n title: string\n description?: string\n slug?: string\n section?: string\n group?: string\n sidebar?: string\n order?: number\n sidebar_position?: number\n content?: string\n last_updated?: string\n draft?: boolean\n authors?: Array<{ id: string; name?: string }>\n tags?: string[]\n redirect_from?: string[]\n reading_time?: number\n word_count?: number\n icon?: string // Icon name for sidebar display (Lucide icon name)\n tab_group?: string // Tab group ID for organizing docs into tabs\n}\n\nexport interface Doc {\n slug: string\n filePath: string // Original file path for sidebar grouping\n title: string\n meta: DocMeta\n content: string\n categoryLabel?: string // Label from _category_.json\n categoryPosition?: number // Position from _category_.json\n categoryCollapsible?: boolean // Collapsible from _category_.json\n categoryCollapsed?: boolean // Default collapsed state from _category_.json\n categoryIcon?: string // Icon from _category_.json\n categoryTabGroup?: string // Tab group from _category_.json\n}\n\nexport interface TocItem {\n id: string\n title: string\n level: number\n}\n\nexport function getVersions(): string[] {\n try {\n const versions = fs.readdirSync(DOCS_DIR)\n return versions.filter((v) => fs.statSync(path.join(DOCS_DIR, v)).isDirectory())\n } catch (error) {\n return [\"v1.0.0\"]\n }\n}\n\n/**\n * Recursively find all MDX files in a directory\n */\nfunction findMdxFiles(dir: string, baseDir: string = dir): string[] {\n const files: string[] = []\n\n try {\n const entries = fs.readdirSync(dir, { withFileTypes: true })\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name)\n\n if (entry.isDirectory()) {\n files.push(...findMdxFiles(fullPath, baseDir))\n } else if (entry.isFile() && entry.name.endsWith(\".mdx\")) {\n // Get relative path from base directory and normalize to forward slashes\n const relativePath = path.relative(baseDir, fullPath).replace(/\\\\/g, '/')\n files.push(relativePath)\n }\n }\n } catch (error) {\n console.error(`Error reading directory ${dir}:`, error)\n }\n\n return files\n}\n\n/**\n * Internal function to read a doc from file path\n */\nfunction readDocFromFile(filePath: string, originalSlug: string): Doc | null {\n try {\n if (!fs.existsSync(filePath)) {\n return null\n }\n\n const fileContents = fs.readFileSync(filePath, \"utf8\")\n const { data, content } = matter(fileContents)\n\n // Calculate reading time\n const { minutes, words } = calculateReadingTime(content)\n\n // If custom slug provided, replace only the filename part, keep the folder structure\n let finalSlug = originalSlug\n if (data.slug) {\n const customSlug = data.slug.replace(/^\\//, '')\n const parts = originalSlug.split(\"/\")\n\n if (parts.length > 1) {\n // Keep folder structure, replace only filename\n parts[parts.length - 1] = customSlug\n finalSlug = parts.join(\"/\")\n } else {\n // Root level file, use custom slug as-is\n finalSlug = customSlug\n }\n }\n\n return {\n slug: finalSlug,\n filePath: originalSlug, // Keep original file path for sidebar\n title: data.title || originalSlug,\n meta: {\n ...data,\n content,\n reading_time: minutes,\n word_count: words,\n } as DocMeta,\n content,\n }\n } catch (error) {\n console.error(`Error reading file ${filePath}:`, error)\n return null\n }\n}\n\nexport async function getDocBySlug(slug: string, version = \"v1.0.0\"): Promise<Doc | null> {\n try {\n // Try direct file first\n let filePath = path.join(DOCS_DIR, version, `${slug}.mdx`)\n let doc = readDocFromFile(filePath, slug)\n\n if (doc) return doc\n\n // If not found, try index.mdx in the folder\n filePath = path.join(DOCS_DIR, version, slug, \"index.mdx\")\n doc = readDocFromFile(filePath, slug)\n\n if (doc) return doc\n\n // If still not found, search all docs for a matching custom slug\n const versionDir = path.join(DOCS_DIR, version)\n if (!fs.existsSync(versionDir)) {\n return null\n }\n\n const mdxFiles = findMdxFiles(versionDir)\n\n for (const file of mdxFiles) {\n const fileSlug = file.replace(/\\.mdx$/, \"\")\n const testPath = path.join(versionDir, file.endsWith(\"index.mdx\") ? file : `${fileSlug}.mdx`)\n const testDoc = readDocFromFile(testPath, fileSlug)\n\n if (testDoc && testDoc.slug === slug) {\n return testDoc\n }\n }\n\n return null\n } catch (error) {\n console.error(`Error reading doc ${slug}:`, error)\n return null\n }\n}\n\nexport async function getAllDocs(version = \"v1.0.0\"): Promise<Doc[]> {\n try {\n const versionDir = path.join(DOCS_DIR, version)\n\n if (!fs.existsSync(versionDir)) {\n return []\n }\n\n const mdxFiles = findMdxFiles(versionDir)\n const categoryConfigs = getAllCategoryConfigs(version)\n\n const docs = await Promise.all(\n mdxFiles.map(async (file) => {\n const originalFilePath = file.replace(/\\.mdx$/, \"\")\n let slug = originalFilePath\n\n // If this is an index.mdx, use the directory path as the slug\n if (file.endsWith(\"/index.mdx\") || file === \"index.mdx\") {\n slug = path.dirname(file).replace(/\\\\/g, '/')\n if (slug === \".\") slug = \"\" // Root index\n }\n\n const doc = await getDocBySlug(slug, version)\n\n // Override filePath to preserve the original file structure\n if (doc) {\n doc.filePath = originalFilePath\n\n // Apply category config if exists\n const folderPath = path.dirname(originalFilePath).replace(/\\\\/g, '/')\n if (folderPath !== \".\") {\n const categoryConfig = categoryConfigs.get(folderPath)\n if (categoryConfig) {\n doc.categoryLabel = categoryConfig.label\n // Use position if available, otherwise fall back to sidebar_position\n doc.categoryPosition = categoryConfig.position ?? categoryConfig.sidebar_position\n doc.categoryCollapsible = categoryConfig.collapsible\n doc.categoryCollapsed = categoryConfig.collapsed\n doc.categoryIcon = categoryConfig.icon\n doc.categoryTabGroup = categoryConfig.tab_group\n }\n }\n }\n\n return doc\n }),\n )\n\n const isDevelopment = process.env.NODE_ENV === \"development\"\n\n // Create a map to track unique slugs and avoid duplicates\n const uniqueDocs = new Map<string, Doc>()\n\n docs\n .filter((doc): doc is Doc => doc !== null)\n // Filter out drafts in production\n .filter((doc) => isDevelopment || !doc.meta.draft)\n .forEach((doc) => {\n // Use the doc's slug (which may be custom from frontmatter) as the key\n uniqueDocs.set(doc.slug, doc)\n })\n\n return Array.from(uniqueDocs.values()).sort((a, b) => {\n const orderA = a.meta.sidebar_position ?? a.meta.order ?? 999\n const orderB = b.meta.sidebar_position ?? b.meta.order ?? 999\n return orderA - orderB\n })\n } catch (error) {\n console.error(`Error getting all docs for version ${version}:`, error)\n return []\n }\n}\n\n// export function getAdjacentDocs(currentSlug: string, allDocs: Doc[]): { previous?: Doc; next?: Doc } {\n// const currentIndex = allDocs.findIndex((doc) => doc.slug === currentSlug)\n\n// if (currentIndex === -1) {\n// return {}\n// }\n\n// return {\n// previous: currentIndex > 0 ? allDocs[currentIndex - 1] : undefined,\n// next: currentIndex < allDocs.length - 1 ? allDocs[currentIndex + 1] : undefined,\n// }\n// }\n\n// Flatten the sidebar structure into a linear order\nfunction flattenSidebarOrder(\n rootGroups: Record<string, SidebarGroup>,\n standalone: Doc[]\n): Doc[] {\n const flatDocs: Doc[] = []\n\n // Recursively flatten groups - intermix folders and files by position\n const flattenGroup = (group: SidebarGroup) => {\n const sortedChildren = sortSidebarGroups(group.children)\n const sortedItems = sortSidebarItems(group.items)\n\n // Merge child groups and items, then sort by position\n const merged: Array<{type: 'group', group: SidebarGroup, position: number} | {type: 'item', doc: Doc, position: number}> = [\n ...sortedChildren.map(([, childGroup]) => ({\n type: 'group' as const,\n group: childGroup,\n position: childGroup.position\n })),\n ...sortedItems.map((doc) => ({\n type: 'item' as const,\n doc,\n position: doc.meta.sidebar_position ?? doc.meta.order ?? 999\n }))\n ]\n\n // Sort by position\n merged.sort((a, b) => a.position - b.position)\n\n // Process in sorted order\n merged.forEach((item) => {\n if (item.type === 'group') {\n flattenGroup(item.group)\n } else {\n flatDocs.push(item.doc)\n }\n })\n }\n\n // Add standalone items first\n sortSidebarItems(standalone).forEach((doc) => {\n flatDocs.push(doc)\n })\n\n // Then add all grouped items\n const sortedRootGroups = sortSidebarGroups(rootGroups)\n sortedRootGroups.forEach(([, group]) => {\n flattenGroup(group)\n })\n\n return flatDocs\n}\n\nexport function getAdjacentDocs(currentSlug: string, allDocs: Doc[]): { previous?: Doc; next?: Doc } {\n // Build the same sidebar structure\n const { rootGroups, standalone } = buildSidebarStructure(allDocs)\n\n // Flatten into the same order as shown in the sidebar\n const orderedDocs = flattenSidebarOrder(rootGroups, standalone)\n\n // Find current doc in the ordered list\n const currentIndex = orderedDocs.findIndex((doc) => doc.slug === currentSlug)\n\n if (currentIndex === -1) {\n return {}\n }\n\n const currentDoc = orderedDocs[currentIndex]\n\n // Get current doc's tab group (from meta or category)\n const currentTabGroup = currentDoc.meta?.tab_group || currentDoc.categoryTabGroup\n\n // Filter docs to match the current doc's tab group status\n // If current has a tab group, only show docs in the same tab group\n // If current has NO tab group, only show docs with NO tab group\n const filteredDocs = orderedDocs.filter((doc) => {\n const docTabGroup = doc.meta?.tab_group || doc.categoryTabGroup\n\n // If current doc has a tab group, only include docs with the same tab group\n if (currentTabGroup) {\n return docTabGroup === currentTabGroup\n }\n\n // If current doc has no tab group, only include docs with no tab group\n return !docTabGroup\n })\n\n // Find current doc's index within the filtered list\n const filteredIndex = filteredDocs.findIndex((doc) => doc.slug === currentSlug)\n\n if (filteredIndex === -1) {\n return {}\n }\n\n return {\n previous: filteredIndex > 0 ? filteredDocs[filteredIndex - 1] : undefined,\n next: filteredIndex < filteredDocs.length - 1 ? filteredDocs[filteredIndex + 1] : undefined,\n }\n}\n\nexport function extractTableOfContents(content: string): TocItem[] {\n const headingRegex = /^(#{2,3})\\s+(.+)$/gm\n const toc: TocItem[] = []\n let match\n\n while ((match = headingRegex.exec(content)) !== null) {\n const level = match[1].length\n const text = match[2]\n // Generate ID the same way rehype-slug does\n const id = text\n .toLowerCase()\n .replace(/\\s+/g, \"-\") // Replace spaces with hyphens first\n .replace(/[^a-z0-9-]/g, \"\") // Remove special chars (dots, slashes, etc)\n .replace(/^-|-$/g, \"\") // Remove leading/trailing hyphens\n\n toc.push({ id, title: text, level })\n }\n\n return toc\n}\n\n/**\n * Check if a slug represents a category (has child documents)\n */\nexport function isCategoryPage(slug: string, allDocs: Doc[]): boolean {\n return allDocs.some((doc) => {\n const parts = doc.slug.split(\"/\")\n const docParent = parts.slice(0, -1).join(\"/\")\n return docParent === slug && doc.slug !== slug\n })\n}\n\n\n","import fs from \"fs\"\nimport path from \"path\"\n\nexport interface CategoryConfig {\n label?: string\n position?: number\n sidebar_position?: number // Alternative naming for position\n link?: {\n type: \"generated-index\" | \"doc\"\n slug?: string\n }\n collapsed?: boolean\n collapsible?: boolean\n icon?: string // Icon name for sidebar display (Lucide icon name)\n tab_group?: string // Tab group ID for organizing categories into tabs\n}\n\nconst DOCS_DIR = path.join(process.cwd(), \"docs\")\n\n/**\n * Read category.json from a folder\n */\nexport function getCategoryConfig(folderPath: string): CategoryConfig | null {\n try {\n const categoryPath = path.join(folderPath, \"_category_.json\")\n\n if (!fs.existsSync(categoryPath)) {\n return null\n }\n\n const content = fs.readFileSync(categoryPath, \"utf8\")\n return JSON.parse(content) as CategoryConfig\n } catch (error) {\n console.error(`Error reading category config from ${folderPath}:`, error)\n return null\n }\n}\n\n/**\n * Get all category configs for a version\n */\nexport function getAllCategoryConfigs(version: string): Map<string, CategoryConfig> {\n const configs = new Map<string, CategoryConfig>()\n const versionDir = path.join(DOCS_DIR, version)\n\n if (!fs.existsSync(versionDir)) {\n return configs\n }\n\n function scanDirectory(dir: string, relativePath: string = \"\") {\n const entries = fs.readdirSync(dir, { withFileTypes: true })\n\n for (const entry of entries) {\n if (entry.isDirectory()) {\n const fullPath = path.join(dir, entry.name)\n const relPath = relativePath ? `${relativePath}/${entry.name}` : entry.name\n\n const config = getCategoryConfig(fullPath)\n if (config) {\n configs.set(relPath, config)\n }\n\n // Recursively scan subdirectories\n scanDirectory(fullPath, relPath)\n }\n }\n }\n\n scanDirectory(versionDir)\n return configs\n}\n","/**\n * Unified sidebar sorting and structure building utilities\n * This module provides consistent sidebar logic across the application\n * to ensure ordering is handled the same way everywhere.\n */\n\nexport interface SidebarGroup {\n label: string\n path: string\n icon?: string\n items: any[]\n position: number\n collapsible: boolean\n defaultCollapsed: boolean\n children: Record<string, SidebarGroup>\n}\n\n/**\n * Sort sidebar items by their position.\n * Items with explicit sidebar_position come first (sorted numerically),\n * followed by items without position (sorted by their original order).\n *\n * @param items - Array of items with optional sidebar_position\n * @returns Sorted array of items\n */\nexport function sortSidebarItems<T extends { sidebar_position?: number; meta?: any }>(items: T[]): T[] {\n return [...items].sort((a, b) => {\n const posA = a.sidebar_position ?? a.meta?.sidebar_position ?? a.meta?.order ?? 999\n const posB = b.sidebar_position ?? b.meta?.sidebar_position ?? b.meta?.order ?? 999\n return posA - posB\n })\n}\n\n/**\n * Sort sidebar groups by their position.\n * Groups with explicit position come first (sorted numerically),\n * followed by groups without position at the end (sorted by their original order).\n *\n * @param groups - Record of group key to group object with position\n * @returns Sorted array of [key, group] tuples\n */\nexport function sortSidebarGroups<T extends { position: number }>(\n groups: Record<string, T>\n): [string, T][] {\n return Object.entries(groups).sort(([, a], [, b]) => {\n const posA = a.position ?? 999\n const posB = b.position ?? 999\n return posA - posB\n })\n}\n\n/**\n * Build hierarchical sidebar structure from flat list of documents\n * This is the single source of truth for sidebar structure used by both\n * the sidebar component and navigation (prev/next) links.\n *\n * @param docs - Array of documents with metadata\n * @returns Object containing root groups and standalone items\n */\nexport function buildSidebarStructure<T extends {\n filePath: string\n slug: string\n categoryLabel?: string\n categoryPosition?: number\n categoryIcon?: string\n categoryCollapsible?: boolean\n categoryCollapsed?: boolean\n meta: any\n}>(docs: T[]): {\n rootGroups: Record<string, SidebarGroup>\n standalone: T[]\n} {\n const rootGroups: Record<string, SidebarGroup> = {}\n const standalone: T[] = []\n\n // First pass: collect category metadata from all docs to build complete folder structure\n const categoryMetadata = new Map<string, {\n label?: string\n position?: number\n icon?: string\n collapsible?: boolean\n collapsed?: boolean\n }>()\n\n docs.forEach((doc) => {\n const pathParts = doc.filePath.split(\"/\")\n const folderPath = pathParts.length > 1 ? pathParts.slice(0, -1).join(\"/\") : \"\"\n\n if (folderPath && doc.categoryLabel) {\n categoryMetadata.set(folderPath, {\n label: doc.categoryLabel,\n position: doc.categoryPosition,\n icon: doc.categoryIcon,\n collapsible: doc.categoryCollapsible,\n collapsed: doc.categoryCollapsed\n })\n }\n })\n\n docs.forEach((doc) => {\n const pathParts = doc.filePath.split(\"/\")\n const isIndexFile = doc.filePath.endsWith(\"/index\") ||\n doc.filePath === \"index\" ||\n (pathParts.length > 1 && doc.slug === pathParts.slice(0, -1).join(\"/\"))\n\n const customGroup = doc.meta.sidebar || doc.meta.group\n\n if (customGroup) {\n const groupName = customGroup.charAt(0).toUpperCase() + customGroup.slice(1)\n if (!rootGroups[groupName]) {\n rootGroups[groupName] = {\n label: groupName,\n path: customGroup,\n items: [],\n position: 999,\n collapsible: doc.categoryCollapsible ?? true,\n defaultCollapsed: doc.categoryCollapsed ?? false,\n children: {}\n }\n }\n if (isIndexFile) {\n // Use categoryPosition if available (from _category_.json), otherwise sidebar_position from frontmatter\n rootGroups[groupName].position = doc.categoryPosition ?? doc.meta.sidebar_position ?? 999\n rootGroups[groupName].icon = doc.categoryIcon\n } else {\n rootGroups[groupName].items.push(doc)\n }\n return\n }\n\n if (pathParts.length > 1) {\n const folderParts = pathParts.slice(0, -1)\n let currentLevel = rootGroups\n let currentPath = \"\"\n\n for (let i = 0; i < folderParts.length; i++) {\n const folder = folderParts[i]\n currentPath = currentPath ? `${currentPath}/${folder}` : folder\n const folderLabel = folder.split(\"-\").map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(\" \")\n\n // Get metadata for this specific folder path\n const metadata = categoryMetadata.get(currentPath)\n\n if (!currentLevel[folder]) {\n currentLevel[folder] = {\n label: metadata?.label ?? folderLabel,\n path: currentPath,\n icon: metadata?.icon,\n items: [],\n position: metadata?.position ?? 999,\n collapsible: metadata?.collapsible ?? true,\n defaultCollapsed: metadata?.collapsed ?? false,\n children: {}\n }\n }\n\n if (i === folderParts.length - 1) {\n if (isIndexFile) {\n // Update position, label, and icon if this is the index file for this folder\n currentLevel[folder].position = doc.categoryPosition ?? doc.meta.sidebar_position ?? currentLevel[folder].position\n if (doc.categoryLabel) {\n currentLevel[folder].label = doc.categoryLabel\n }\n if (doc.categoryIcon) {\n currentLevel[folder].icon = doc.categoryIcon\n }\n if (doc.categoryCollapsible !== undefined) {\n currentLevel[folder].collapsible = doc.categoryCollapsible\n }\n if (doc.categoryCollapsed !== undefined) {\n currentLevel[folder].defaultCollapsed = doc.categoryCollapsed\n }\n } else {\n currentLevel[folder].items.push(doc)\n }\n }\n\n currentLevel = currentLevel[folder].children\n }\n } else {\n if (!isIndexFile) {\n standalone.push(doc)\n }\n }\n })\n\n return { rootGroups, standalone }\n}\n","/**\n * Caching layer for MDX operations to improve development performance\n *\n * This module provides in-memory caching for expensive file system operations\n * that occur during static generation. In development mode, caches are\n * invalidated automatically when files change.\n */\n\nimport { Doc, getVersions, getAllDocs, getDocBySlug } from './mdx'\nimport { watch } from 'fs'\nimport { join } from 'path'\nimport { PerfTimer, logCacheOperation } from './dev-utils'\n\nconst isDevelopment = process.env.NODE_ENV === 'development'\n\n// Cache stores\nconst versionsCache = {\n data: null as string[] | null,\n timestamp: 0,\n}\n\nconst allDocsCache = new Map<string, {\n data: Doc[]\n timestamp: number\n}>()\n\nconst docBySlugCache = new Map<string, {\n data: Doc | null\n timestamp: number\n}>()\n\n// Cache TTL (time to live) in milliseconds\nconst CACHE_TTL = isDevelopment ? 5000 : 60000 // 5s in dev, 60s in prod\n\n// Track if we've set up file watchers\nlet watchersInitialized = false\n\n/**\n * Initialize file watchers to invalidate cache on changes\n * Only runs in development mode\n */\nfunction initializeWatchers() {\n if (!isDevelopment || watchersInitialized) return\n\n watchersInitialized = true\n const docsPath = join(process.cwd(), 'docs')\n\n try {\n watch(docsPath, { recursive: true }, (eventType, filename) => {\n if (!filename) return\n\n // Invalidate relevant caches when MDX or JSON files change\n if (filename.endsWith('.mdx') || filename.endsWith('.json')) {\n // Extract version from path\n const parts = filename.split(/[/\\\\]/)\n const version = parts[0]\n\n // Clear all docs cache for this version\n allDocsCache.delete(version)\n\n // Clear individual doc caches for this version\n const cacheKeysToDelete: string[] = []\n docBySlugCache.forEach((_, key) => {\n if (key.startsWith(`${version}:`)) {\n cacheKeysToDelete.push(key)\n }\n })\n cacheKeysToDelete.forEach(key => docBySlugCache.delete(key))\n\n // Clear versions cache if directory structure changed\n if (eventType === 'rename') {\n versionsCache.data = null\n }\n\n console.log(`[MDX Cache] Invalidated cache for: ${filename}`)\n }\n })\n\n console.log('[MDX Cache] File watchers initialized')\n } catch (error) {\n console.error('[MDX Cache] Failed to initialize watchers:', error)\n }\n}\n\n/**\n * Check if a cache entry is still valid\n */\nfunction isCacheValid(timestamp: number): boolean {\n return Date.now() - timestamp < CACHE_TTL\n}\n\n/**\n * Cached version of getVersions()\n */\nexport function getCachedVersions(): string[] {\n // Initialize watchers on first use\n initializeWatchers()\n\n if (versionsCache.data && isCacheValid(versionsCache.timestamp)) {\n logCacheOperation('hit', 'versions')\n return versionsCache.data\n }\n\n logCacheOperation('miss', 'versions')\n const timer = new PerfTimer('getVersions')\n const versions = getVersions()\n timer.end()\n\n versionsCache.data = versions\n versionsCache.timestamp = Date.now()\n\n return versions\n}\n\n/**\n * Cached version of getAllDocs()\n */\nexport async function getCachedAllDocs(version = 'v1.0.0'): Promise<Doc[]> {\n // Initialize watchers on first use\n initializeWatchers()\n\n const cached = allDocsCache.get(version)\n if (cached && isCacheValid(cached.timestamp)) {\n logCacheOperation('hit', `getAllDocs:${version}`)\n return cached.data\n }\n\n logCacheOperation('miss', `getAllDocs:${version}`)\n const timer = new PerfTimer(`getAllDocs(${version})`)\n const docs = await getAllDocs(version)\n timer.end()\n\n allDocsCache.set(version, {\n data: docs,\n timestamp: Date.now(),\n })\n\n return docs\n}\n\n/**\n * Cached version of getDocBySlug()\n */\nexport async function getCachedDocBySlug(\n slug: string,\n version = 'v1.0.0'\n): Promise<Doc | null> {\n // Initialize watchers on first use\n initializeWatchers()\n\n const cacheKey = `${version}:${slug}`\n const cached = docBySlugCache.get(cacheKey)\n\n if (cached && isCacheValid(cached.timestamp)) {\n logCacheOperation('hit', `getDocBySlug:${cacheKey}`)\n return cached.data\n }\n\n logCacheOperation('miss', `getDocBySlug:${cacheKey}`)\n const timer = new PerfTimer(`getDocBySlug(${slug})`)\n const doc = await getDocBySlug(slug, version)\n timer.end()\n\n docBySlugCache.set(cacheKey, {\n data: doc,\n timestamp: Date.now(),\n })\n\n return doc\n}\n\n/**\n * Manually clear all caches\n * Useful for testing or when you want to force a refresh\n */\nexport function clearAllCaches() {\n versionsCache.data = null\n allDocsCache.clear()\n docBySlugCache.clear()\n console.log('[MDX Cache] All caches cleared')\n}\n\n/**\n * Get cache statistics for debugging\n */\nexport function getCacheStats() {\n return {\n versions: {\n cached: versionsCache.data !== null,\n age: versionsCache.timestamp ? Date.now() - versionsCache.timestamp : 0,\n },\n allDocs: {\n entries: allDocsCache.size,\n versions: Array.from(allDocsCache.keys()),\n },\n docBySlug: {\n entries: docBySlugCache.size,\n },\n }\n}\n","/**\n * Development utilities for debugging and performance monitoring\n * Only active in development mode\n */\n\nconst isDevelopment = process.env.NODE_ENV === 'development'\n\n/**\n * Performance timer for measuring operation duration\n */\nexport class PerfTimer {\n private startTime: number\n private label: string\n\n constructor(label: string) {\n this.label = label\n this.startTime = isDevelopment ? performance.now() : 0\n }\n\n end() {\n if (!isDevelopment) return\n\n const duration = performance.now() - this.startTime\n const color = duration > 1000 ? '\\x1b[31m' : duration > 500 ? '\\x1b[33m' : '\\x1b[32m'\n const reset = '\\x1b[0m'\n\n console.log(`${color}⏱️ ${this.label}: ${duration.toFixed(2)}ms${reset}`)\n }\n}\n\n/**\n * Log file system operations\n */\nexport function logFsOperation(operation: string, path: string, details?: any) {\n if (!isDevelopment) return\n\n console.log(`📁 [FS] ${operation}: ${path}`, details || '')\n}\n\n/**\n * Log cache operations\n */\nexport function logCacheOperation(operation: 'hit' | 'miss' | 'invalidate', key: string) {\n if (!isDevelopment) return\n\n const emoji = operation === 'hit' ? '✅' : operation === 'miss' ? '❌' : '🔄'\n console.log(`${emoji} [Cache] ${operation}: ${key}`)\n}\n\n/**\n * Memory usage reporter\n */\nexport function logMemoryUsage(label?: string) {\n if (!isDevelopment) return\n\n const used = process.memoryUsage()\n const prefix = label ? `[${label}] ` : ''\n\n console.log(`💾 ${prefix}Memory Usage:`, {\n rss: `${Math.round(used.rss / 1024 / 1024)}MB`,\n heapTotal: `${Math.round(used.heapTotal / 1024 / 1024)}MB`,\n heapUsed: `${Math.round(used.heapUsed / 1024 / 1024)}MB`,\n })\n}\n\n/**\n * Pretty print object for debugging\n */\nexport function debugLog(label: string, data: any) {\n if (!isDevelopment) return\n\n console.log(`\\n🔍 ${label}:`)\n console.dir(data, { depth: null, colors: true })\n console.log('')\n}\n","export interface TOCItem {\n id: string\n title: string\n level: number\n}\n\n/**\n * Extract headings from HTML string for table of contents\n */\nexport function extractHeadings(html: string): TOCItem[] {\n const headingRegex = /<h([2-3])[^>]*id=\"([^\"]*)\"[^>]*>(.*?)<\\/h\\1>/g\n const toc: TOCItem[] = []\n let match\n\n while ((match = headingRegex.exec(html)) !== null) {\n const level = Number.parseInt(match[1])\n const id = match[2]\n const title = match[3].replace(/<[^>]*>/g, \"\") // Strip HTML tags\n\n toc.push({ id, title, level })\n }\n\n return toc\n}\n","/**\n * Configuration schema for Specra documentation system\n */\n\n/**\n * Site metadata and branding\n */\nexport interface SiteConfig {\n /** The title of the documentation site */\n title: string\n /** Short description of the documentation */\n description?: string\n /** URL where the documentation is hosted */\n url?: string\n /** Base URL path for the documentation (e.g., '/docs') */\n baseUrl?: string\n /** Path to the site logo */\n logo?: string\n /** Path to the favicon */\n favicon?: string\n /** Default language for the documentation */\n language?: string\n /** Organization or author name */\n organizationName?: string\n /** Project name */\n projectName?: string\n /** Active/default version for the documentation */\n activeVersion?: string\n}\n\n/**\n * Theme and appearance settings\n */\nexport interface ThemeConfig {\n /** Primary color for the theme */\n primaryColor?: string\n /** Default theme mode */\n defaultMode?: \"light\" | \"dark\" | \"system\"\n /** Whether to respect system preferences */\n respectPrefersColorScheme?: boolean\n /** Custom CSS file path */\n customCss?: string\n}\n\n/**\n * Tab group for organizing documentation\n */\nexport interface TabGroup {\n /** Unique identifier for the tab group */\n id: string\n /** Display label for the tab */\n label: string\n /** Optional icon name (lucide-react icon) */\n icon?: string\n}\n\n/**\n * Navigation and sidebar configuration\n */\nexport interface NavigationConfig {\n /** Whether to show the sidebar by default */\n showSidebar?: boolean\n /** Whether the sidebar is collapsible */\n collapsibleSidebar?: boolean\n /** Whether to show breadcrumbs */\n showBreadcrumbs?: boolean\n /** Whether to show table of contents */\n showTableOfContents?: boolean\n /** Position of table of contents */\n tocPosition?: \"left\" | \"right\"\n /** Maximum depth for table of contents */\n tocMaxDepth?: number\n /** Tab groups for organizing documentation sections */\n tabGroups?: TabGroup[]\n}\n\n/**\n * Social and external links\n */\nexport interface SocialLinks {\n /** GitHub repository URL */\n github?: string\n /** Twitter/X handle or URL */\n twitter?: string\n /** Discord invite URL */\n discord?: string\n /** LinkedIn profile or company page */\n linkedin?: string\n /** YouTube channel URL */\n youtube?: string\n /** Custom social links */\n custom?: Array<{\n label: string\n url: string\n icon?: string\n }>\n}\n\n/**\n * Search configuration\n */\nexport interface SearchConfig {\n /** Enable/disable search functionality */\n enabled?: boolean\n /** Placeholder text for search input */\n placeholder?: string\n /** Search provider type */\n provider?: \"meilisearch\" | \"algolia\" | \"local\"\n /** Meilisearch configuration */\n meilisearch?: {\n /** Meilisearch server URL */\n host: string\n /** API key for Meilisearch */\n apiKey?: string\n /** Index name */\n indexName: string\n }\n}\n\n/**\n * Analytics configuration\n */\nexport interface AnalyticsConfig {\n /** Google Analytics tracking ID */\n googleAnalytics?: string\n /** Google Tag Manager ID */\n googleTagManager?: string\n /** Plausible Analytics domain */\n plausible?: string\n /** Custom analytics scripts */\n custom?: Array<{\n src: string\n async?: boolean\n defer?: boolean\n }>\n}\n\n/**\n * Footer configuration\n */\nexport interface FooterConfig {\n /** Copyright text */\n copyright?: string\n /** Footer links organized by columns */\n links?: Array<{\n title: string\n items: Array<{\n label: string\n href: string\n }>\n }>\n /** Custom footer content */\n customContent?: string\n}\n\n/**\n * Documentation features\n */\nexport interface FeaturesConfig {\n /** Enable/disable edit this page links */\n editUrl?: string | false\n /** Show last updated timestamp */\n showLastUpdated?: boolean\n /** Show reading time estimate */\n showReadingTime?: boolean\n /** Show author information */\n showAuthors?: boolean\n /** Show tags */\n showTags?: boolean\n /** Enable version dropdown */\n versioning?: boolean\n /** Enable i18n (internationalization) */\n i18n?: boolean\n}\n\n/**\n * Site-wide banner configuration\n */\nexport interface BannerConfig {\n /** Whether the banner is enabled */\n enabled?: boolean\n /** Banner message */\n message?: string\n /** Banner type */\n type?: \"info\" | \"warning\" | \"success\" | \"error\"\n /** Whether the banner can be dismissed */\n dismissible?: boolean\n}\n\n/**\n * Environment variables that can be used in documentation\n * These will be replaced at build time or runtime\n */\nexport interface EnvironmentVariables {\n /** API base URL */\n API_BASE_URL?: string\n /** API version */\n API_VERSION?: string\n /** CDN URL */\n CDN_URL?: string\n /** Custom environment variables */\n [key: string]: string | undefined\n}\n\n/**\n * Deployment configuration for different hosting scenarios\n */\nexport interface DeploymentConfig {\n /**\n * Deployment target\n * - 'vercel': For Vercel or similar Node.js hosting (uses 'standalone' output)\n * - 'github-pages': For GitHub Pages static hosting (uses 'export' output)\n * - 'static': For any static hosting like Netlify, Cloudflare Pages, etc.\n * - 'custom-domain-static': For static hosting with custom domain (no basePath needed)\n */\n target?: \"vercel\" | \"github-pages\" | \"static\" | \"custom-domain-static\"\n\n /**\n * Base path for assets when deploying to GitHub Pages without custom domain\n * This should be your repository name (e.g., 'my-repo')\n * Only used when target is 'github-pages' and no custom domain is configured\n */\n basePath?: string\n\n /**\n * Whether a custom domain is configured\n * When true, basePath will be ignored even for GitHub Pages\n */\n customDomain?: boolean\n}\n\n/**\n * Main configuration interface\n */\nexport interface SpecraConfig {\n /** Site metadata and branding */\n site: SiteConfig\n /** Theme and appearance settings */\n theme?: ThemeConfig\n /** Navigation and sidebar configuration */\n navigation?: NavigationConfig\n /** Social and external links */\n social?: SocialLinks\n /** Search configuration */\n search?: SearchConfig\n /** Analytics configuration */\n analytics?: AnalyticsConfig\n /** Footer configuration */\n footer?: FooterConfig\n /** Site-wide banner */\n banner?: BannerConfig\n /** Documentation features */\n features?: FeaturesConfig\n /** Environment variables for use in docs */\n env?: EnvironmentVariables\n /** Deployment configuration */\n deployment?: DeploymentConfig\n}\n\n/**\n * Default configuration values\n */\nexport const defaultConfig: SpecraConfig = {\n site: {\n title: \"Documentation\",\n description: \"Project documentation\",\n baseUrl: \"/\",\n language: \"en\",\n },\n theme: {\n defaultMode: \"system\",\n respectPrefersColorScheme: true,\n },\n navigation: {\n showSidebar: true,\n collapsibleSidebar: true,\n showBreadcrumbs: true,\n showTableOfContents: true,\n tocPosition: \"right\",\n tocMaxDepth: 3,\n },\n search: {\n enabled: true,\n provider: \"local\",\n placeholder: \"Search documentation...\",\n },\n features: {\n showLastUpdated: true,\n showReadingTime: true,\n showAuthors: false,\n showTags: true,\n versioning: true,\n i18n: false,\n },\n}\n","import specraConfigJson from \"../specra.config.json\"\nimport { SpecraConfig, defaultConfig } from \"./config.types\"\n\n/**\n * Deep merge two objects\n */\nfunction deepMerge<T extends Record<string, any>>(target: T, source: Partial<T>): T {\n const result = { ...target }\n\n for (const key in source) {\n const sourceValue = source[key]\n const targetValue = result[key]\n\n if (sourceValue && typeof sourceValue === \"object\" && !Array.isArray(sourceValue)) {\n result[key] = deepMerge(\n targetValue && typeof targetValue === \"object\" ? targetValue : {},\n sourceValue,\n ) as T[Extract<keyof T, string>]\n } else if (sourceValue !== undefined) {\n result[key] = sourceValue as T[Extract<keyof T, string>]\n }\n }\n\n return result\n}\n\n/**\n * Load and parse the Specra configuration file\n * Falls back to default configuration if file doesn't exist or is invalid\n */\nexport function loadConfig(userConfig: Partial<SpecraConfig>): SpecraConfig {\n try {\n // const userConfig = specraConfigJson as unknown as Partial<SpecraConfig>\n\n // Merge user config with defaults \n const config = deepMerge(defaultConfig, userConfig)\n\n return config\n } catch (error) {\n console.error(`❌ Error loading configuration:`, error)\n console.warn(\"Using default configuration.\")\n return defaultConfig\n }\n}\n\n/**\n * Get a specific configuration value by path (SERVER ONLY)\n * Example: getConfigValue('site.title') or getConfigValue('theme.defaultMode')\n */\nexport function getConfigValue<T = any>(path: string, config?: SpecraConfig): T | undefined {\n const cfg = config || loadConfig({})\n const keys = path.split(\".\")\n let value: any = cfg\n\n for (const key of keys) {\n if (value && typeof value === \"object\" && key in value) {\n value = value[key]\n } else {\n return undefined\n }\n }\n\n return value as T\n}\n\n/**\n * Replace environment variables in a string (SERVER ONLY)\n * Supports ${ENV_VAR} and {{ENV_VAR}} syntax\n */\nexport function replaceEnvVariables(text: string, config?: SpecraConfig): string {\n const cfg = config || loadConfig({})\n const envVars = cfg.env || {}\n\n let result = text\n\n // Replace ${VAR} syntax\n result = result.replace(/\\$\\{([^}]+)\\}/g, (match, varName) => {\n return envVars[varName] || match\n })\n\n // Replace {{VAR}} syntax\n result = result.replace(/\\{\\{([^}]+)\\}\\}/g, (match, varName) => {\n return envVars[varName] || match\n })\n\n return result\n}\n\n/**\n * Process content and replace all environment variables (SERVER ONLY)\n */\nexport function processContentWithEnv(content: string, config?: SpecraConfig): string {\n return replaceEnvVariables(content, config)\n}\n\n/**\n * Validate configuration (basic validation) (SERVER ONLY)\n */\nexport function validateConfig(config: SpecraConfig): { valid: boolean; errors: string[] } {\n const errors: string[] = []\n\n // Required fields\n if (!config.site?.title) {\n errors.push(\"site.title is required\")\n }\n\n // URL validation\n if (config.site?.url) {\n try {\n new URL(config.site.url)\n } catch {\n errors.push(\"site.url must be a valid URL\")\n }\n }\n\n // Social links validation\n if (config.social) {\n const socialKeys = [\"github\", \"twitter\", \"discord\", \"linkedin\", \"youtube\"] as const\n for (const key of socialKeys) {\n const url = config.social[key]\n if (url) {\n try {\n new URL(url)\n } catch {\n errors.push(`social.${key} must be a valid URL`)\n }\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n }\n}\n\n// Singleton instance\nlet configInstance: SpecraConfig | null = null\n\nexport function initConfig(userConfig: Partial<SpecraConfig>): SpecraConfig {\n if (configInstance) {\n throw new Error(\"Specra config has already been initialized\")\n }\n\n configInstance = loadConfig(userConfig)\n return configInstance\n}\n\n/**\n * Get the configuration instance (cached) (SERVER ONLY)\n */\nexport function getConfig(): SpecraConfig {\n if (!configInstance) {\n throw new Error(\"Specra config has not been initialized\")\n }\n return configInstance\n}\n\n/**\n * Reload the configuration (useful for development) (SERVER ONLY)\n */\nexport function reloadConfig(userConfig: Partial<SpecraConfig>): SpecraConfig {\n configInstance = loadConfig(userConfig)\n return configInstance\n}\n\n/**\n * Export the loaded config as default (SERVER ONLY)\n */\n// export default getConfig()\n","import type { SpecraApiSpec } from \"../api-parser.types\"\nimport type { ApiSpecParser } from \"./base-parser\"\n\n/**\n * Parser for native Specra API format\n * This is a pass-through parser since the input is already in the correct format\n */\nexport class SpecraParser implements ApiSpecParser {\n validate(input: any): boolean {\n return (\n typeof input === \"object\" &&\n input !== null &&\n \"endpoints\" in input &&\n Array.isArray(input.endpoints)\n )\n }\n\n parse(input: any): SpecraApiSpec {\n if (!this.validate(input)) {\n throw new Error(\"Invalid Specra API spec format\")\n }\n return input as SpecraApiSpec\n }\n}\n","import type { SpecraApiSpec, ApiEndpointSpec, ApiParam, ApiResponse } from \"../api-parser.types\"\nimport type { ApiSpecParser } from \"./base-parser\"\n\n/**\n * Parser for OpenAPI 3.0/3.1 specifications\n */\nexport class OpenApiParser implements ApiSpecParser {\n validate(input: any): boolean {\n return (\n typeof input === \"object\" &&\n input !== null &&\n (\"openapi\" in input || \"swagger\" in input) &&\n \"paths\" in input\n )\n }\n\n parse(input: any): SpecraApiSpec {\n if (!this.validate(input)) {\n throw new Error(\"Invalid OpenAPI spec format\")\n }\n\n const baseUrl = this.extractBaseUrl(input)\n const endpoints: ApiEndpointSpec[] = []\n\n // Parse paths\n for (const [path, pathItem] of Object.entries(input.paths || {})) {\n const methods = [\"get\", \"post\", \"put\", \"patch\", \"delete\"] as const\n\n for (const method of methods) {\n const operation = (pathItem as any)[method]\n if (!operation) continue\n\n const endpoint = this.parseOperation(path, method.toUpperCase() as any, operation, input)\n endpoints.push(endpoint)\n }\n }\n\n return {\n version: input.info?.version,\n title: input.info?.title,\n description: input.info?.description,\n baseUrl,\n auth: this.extractAuth(input),\n endpoints,\n }\n }\n\n private extractBaseUrl(spec: any): string {\n // OpenAPI 3.x servers\n if (spec.servers && spec.servers.length > 0) {\n return spec.servers[0].url\n }\n\n // Swagger 2.0\n if (spec.host) {\n const scheme = spec.schemes?.[0] || \"https\"\n const basePath = spec.basePath || \"\"\n return `${scheme}://${spec.host}${basePath}`\n }\n\n return \"\"\n }\n\n private extractAuth(spec: any): SpecraApiSpec[\"auth\"] {\n const securitySchemes = spec.components?.securitySchemes || spec.securityDefinitions\n\n if (!securitySchemes) return undefined\n\n // Get the first security scheme\n const firstScheme = Object.values(securitySchemes)[0] as any\n if (!firstScheme) return undefined\n\n if (firstScheme.type === \"http\" && firstScheme.scheme === \"bearer\") {\n return {\n type: \"bearer\",\n description: firstScheme.description,\n tokenPrefix: \"Bearer\",\n }\n }\n\n if (firstScheme.type === \"apiKey\") {\n return {\n type: \"apiKey\",\n description: firstScheme.description,\n headerName: firstScheme.name || \"X-API-Key\",\n }\n }\n\n if (firstScheme.type === \"http\" && firstScheme.scheme === \"basic\") {\n return {\n type: \"basic\",\n description: firstScheme.description,\n }\n }\n\n return undefined\n }\n\n private parseOperation(\n path: string,\n method: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\",\n operation: any,\n spec: any\n ): ApiEndpointSpec {\n const endpoint: ApiEndpointSpec = {\n title: operation.summary || operation.operationId || `${method} ${path}`,\n method,\n path: this.convertPathParams(path),\n description: operation.description,\n }\n\n // Parse parameters\n const params = this.parseParameters(operation.parameters || [], spec)\n if (params.path.length > 0) endpoint.pathParams = params.path\n if (params.query.length > 0) endpoint.queryParams = params.query\n if (params.header.length > 0) {\n endpoint.headers = params.header.map((p) => ({\n name: p.name,\n value: p.example || \"\",\n description: p.description,\n }))\n }\n\n // Parse request body\n if (operation.requestBody) {\n endpoint.body = this.parseRequestBody(operation.requestBody, spec)\n }\n\n // Parse responses\n const responses = this.parseResponses(operation.responses || {}, spec)\n if (responses.success) endpoint.successResponse = responses.success\n if (responses.errors.length > 0) endpoint.errorResponses = responses.errors\n\n return endpoint\n }\n\n private convertPathParams(path: string): string {\n // Convert OpenAPI {param} to :param\n return path.replace(/\\{([^}]+)\\}/g, \":$1\")\n }\n\n private parseParameters(\n parameters: any[],\n spec: any\n ): { path: ApiParam[]; query: ApiParam[]; header: ApiParam[] } {\n const result = { path: [] as ApiParam[], query: [] as ApiParam[], header: [] as ApiParam[] }\n\n for (const param of parameters) {\n // Resolve $ref if present\n const resolved = param.$ref ? this.resolveRef(param.$ref, spec) : param\n\n const apiParam: ApiParam = {\n name: resolved.name,\n type: resolved.schema?.type || resolved.type || \"string\",\n required: resolved.required,\n description: resolved.description,\n example: resolved.example || resolved.schema?.example,\n }\n\n if (resolved.in === \"path\") result.path.push(apiParam)\n else if (resolved.in === \"query\") result.query.push(apiParam)\n else if (resolved.in === \"header\") result.header.push(apiParam)\n }\n\n return result\n }\n\n private parseRequestBody(requestBody: any, spec: any): ApiEndpointSpec[\"body\"] {\n const content = requestBody.content?.[\"application/json\"]\n if (!content) return undefined\n\n return {\n description: requestBody.description,\n example: content.example || this.generateExample(content.schema, spec),\n schema: content.schema,\n }\n }\n\n private parseResponses(\n responses: any,\n spec: any\n ): { success?: ApiResponse; errors: ApiResponse[] } {\n const result: { success?: ApiResponse; errors: ApiResponse[] } = { errors: [] }\n\n for (const [statusCode, response] of Object.entries(responses)) {\n const status = parseInt(statusCode)\n if (isNaN(status)) continue\n\n const resolved = (response as any).$ref ? this.resolveRef((response as any).$ref, spec) : response\n const content = (resolved as any).content?.[\"application/json\"]\n\n const apiResponse: ApiResponse = {\n status,\n description: (resolved as any).description,\n example: content?.example || this.generateExample(content?.schema, spec),\n schema: content?.schema,\n }\n\n if (status >= 200 && status < 300) {\n result.success = apiResponse\n } else {\n result.errors.push(apiResponse)\n }\n }\n\n return result\n }\n\n private generateExample(schema: any, spec: any): any {\n if (!schema) return undefined\n if (schema.$ref) schema = this.resolveRef(schema.$ref, spec)\n if (schema.example) return schema.example\n\n // Simple example generation based on schema type\n if (schema.type === \"object\" && schema.properties) {\n const example: any = {}\n for (const [key, prop] of Object.entries(schema.properties)) {\n example[key] = this.generateExample(prop, spec)\n }\n return example\n }\n\n if (schema.type === \"array\" && schema.items) {\n return [this.generateExample(schema.items, spec)]\n }\n\n // Default values by type\n const defaults: any = {\n string: \"string\",\n number: 0,\n integer: 0,\n boolean: false,\n object: {},\n array: [],\n }\n\n return defaults[schema.type] || null\n }\n\n private resolveRef(ref: string, spec: any): any {\n const path = ref.replace(/^#\\//, \"\").split(\"/\")\n let current = spec\n\n for (const segment of path) {\n current = current[segment]\n if (!current) return {}\n }\n\n return current\n }\n}\n","import type { SpecraApiSpec, ApiEndpointSpec, ApiParam, ApiHeader } from \"../api-parser.types\"\nimport type { ApiSpecParser } from \"./base-parser\"\n\n/**\n * Parser for Postman Collection v2.0/v2.1\n */\nexport class PostmanParser implements ApiSpecParser {\n validate(input: any): boolean {\n return (\n typeof input === \"object\" &&\n input !== null &&\n \"info\" in input &&\n input.info?.schema?.includes(\"v2\")\n )\n }\n\n parse(input: any): SpecraApiSpec {\n if (!this.validate(input)) {\n throw new Error(\"Invalid Postman Collection format (requires v2.0 or v2.1)\")\n }\n\n const baseUrl = this.extractBaseUrl(input)\n const endpoints: ApiEndpointSpec[] = []\n\n // Parse items (can be nested in folders)\n this.parseItems(input.item || [], endpoints, baseUrl, input)\n\n return {\n version: input.info?.version,\n title: input.info?.name,\n description: input.info?.description,\n baseUrl,\n auth: this.extractAuth(input.auth),\n globalHeaders: this.extractGlobalHeaders(input),\n endpoints,\n }\n }\n\n private extractBaseUrl(collection: any): string {\n // Try to get from variables\n const baseUrlVar = collection.variable?.find(\n (v: any) => v.key === \"baseUrl\" || v.key === \"base_url\" || v.key === \"url\"\n )\n if (baseUrlVar) return baseUrlVar.value\n\n // Try to extract from first request\n if (collection.item && collection.item.length > 0) {\n const firstRequest = this.findFirstRequest(collection.item)\n if (firstRequest?.request?.url) {\n const url = this.parseUrl(firstRequest.request.url)\n if (url.host) {\n return `${url.protocol}://${url.host.join(\".\")}`\n }\n }\n }\n\n return \"\"\n }\n\n private findFirstRequest(items: any[]): any {\n for (const item of items) {\n if (item.request) return item\n if (item.item) {\n const found = this.findFirstRequest(item.item)\n if (found) return found\n }\n }\n return null\n }\n\n private extractAuth(auth: any): SpecraApiSpec[\"auth\"] {\n if (!auth) return undefined\n\n if (auth.type === \"bearer\") {\n return {\n type: \"bearer\",\n tokenPrefix: \"Bearer\",\n }\n }\n\n if (auth.type === \"apikey\") {\n const keyData = auth.apikey?.find((a: any) => a.key === \"key\")\n const keyName = keyData?.value || \"X-API-Key\"\n\n return {\n type: \"apiKey\",\n headerName: keyName,\n }\n }\n\n if (auth.type === \"basic\") {\n return {\n type: \"basic\",\n }\n }\n\n return undefined\n }\n\n private extractGlobalHeaders(collection: any): ApiHeader[] {\n // Postman doesn't have global headers in the same way, but we can check for common patterns\n return []\n }\n\n private parseItems(items: any[], endpoints: ApiEndpointSpec[], baseUrl: string, collection: any) {\n for (const item of items) {\n // If it's a folder, recurse\n if (item.item && Array.isArray(item.item)) {\n this.parseItems(item.item, endpoints, baseUrl, collection)\n }\n // If it's a request\n else if (item.request) {\n const endpoint = this.parseRequest(item, baseUrl, collection)\n endpoints.push(endpoint)\n }\n }\n }\n\n private parseRequest(item: any, baseUrl: string, collection: any): ApiEndpointSpec {\n const request = item.request\n const url = this.parseUrl(request.url)\n\n const endpoint: ApiEndpointSpec = {\n title: item.name,\n method: request.method.toUpperCase(),\n path: this.buildPath(url, baseUrl),\n description: item.request.description || item.description,\n }\n\n // Parse URL parameters (path and query)\n const params = this.parseUrlParams(url)\n if (params.path.length > 0) endpoint.pathParams = params.path\n if (params.query.length > 0) endpoint.queryParams = params.query\n\n // Parse headers\n if (request.header && request.header.length > 0) {\n endpoint.headers = request.header\n .filter((h: any) => !h.disabled)\n .map((h: any) => ({\n name: h.key,\n value: h.value || \"\",\n description: h.description,\n }))\n }\n\n // Parse request body\n if (request.body) {\n endpoint.body = this.parseBody(request.body)\n }\n\n // Parse response examples\n const responses = this.parseResponses(item.response || [])\n if (responses.success) endpoint.successResponse = responses.success\n if (responses.errors.length > 0) endpoint.errorResponses = responses.errors\n\n return endpoint\n }\n\n private parseUrl(url: any): {\n protocol: string\n host: string[]\n path: string[]\n query: any[]\n variable: any[]\n } {\n if (typeof url === \"string\") {\n // Parse string URL\n const urlObj = new URL(url)\n return {\n protocol: urlObj.protocol.replace(\":\", \"\"),\n host: urlObj.hostname.split(\".\"),\n path: urlObj.pathname.split(\"/\").filter(Boolean),\n query: [],\n variable: [],\n }\n }\n\n return {\n protocol: url.protocol || \"https\",\n host: url.host || [],\n path: url.path || [],\n query: url.query || [],\n variable: url.variable || [],\n }\n }\n\n private buildPath(url: any, baseUrl: string): string {\n let path = \"/\"\n\n if (url.path && url.path.length > 0) {\n path += url.path.join(\"/\")\n }\n\n // Convert Postman :param to our :param format (they're the same!)\n // But we need to handle {{variable}} syntax\n path = path.replace(/\\{\\{([^}]+)\\}\\}/g, \":$1\")\n\n return path\n }\n\n private parseUrlParams(url: any): { path: ApiParam[]; query: ApiParam[] } {\n const result = { path: [] as ApiParam[], query: [] as ApiParam[] }\n\n // Path parameters from variables\n if (url.variable && url.variable.length > 0) {\n for (const v of url.variable) {\n result.path.push({\n name: v.key,\n type: v.type || \"string\",\n description: v.description,\n example: v.value,\n })\n }\n }\n\n // Extract path params from the path itself\n if (url.path && url.path.length > 0) {\n for (const segment of url.path) {\n if (segment.startsWith(\":\")) {\n const paramName = segment.slice(1)\n // Only add if not already added from variables\n if (!result.path.find((p) => p.name === paramName)) {\n result.path.push({\n name: paramName,\n type: \"string\",\n })\n }\n }\n }\n }\n\n // Query parameters\n if (url.query && url.query.length > 0) {\n for (const q of url.query) {\n if (q.disabled) continue\n result.query.push({\n name: q.key,\n type: \"string\",\n description: q.description,\n example: q.value,\n })\n }\n }\n\n return result\n }\n\n private parseBody(body: any): ApiEndpointSpec[\"body\"] {\n if (!body) return undefined\n\n let example: any\n let description = body.description\n\n if (body.mode === \"raw\") {\n try {\n example = JSON.parse(body.raw)\n } catch {\n example = body.raw\n }\n } else if (body.mode === \"formdata\" || body.mode === \"urlencoded\") {\n example = {}\n for (const item of body[body.mode] || []) {\n if (!item.disabled) {\n example[item.key] = item.value\n }\n }\n }\n\n return {\n description,\n example,\n }\n }\n\n private parseResponses(responses: any[]): { success?: any; errors: any[] } {\n const result: { success?: any; errors: any[] } = { errors: [] }\n\n for (const response of responses) {\n let example: any\n try {\n example = JSON.parse(response.body)\n } catch {\n example = response.body\n }\n\n const apiResponse = {\n status: response.code || 200,\n description: response.name,\n example,\n }\n\n if (apiResponse.status >= 200 && apiResponse.status < 300) {\n if (!result.success) result.success = apiResponse\n } else {\n result.errors.push(apiResponse)\n }\n }\n\n return result\n }\n}\n","import type { SpecraApiSpec } from \"../api-parser.types\"\nimport type { ApiSpecParser } from \"./base-parser\"\nimport { SpecraParser } from \"./specra-parser\"\nimport { OpenApiParser } from \"./openapi-parser\"\nimport { PostmanParser } from \"./postman-parser\"\n\nexport type ParserType = \"auto\" | \"specra\" | \"openapi\" | \"postman\"\n\n/**\n * Registry of all available parsers\n */\nconst parsers: Map<string, ApiSpecParser> = new Map([\n [\"specra\", new SpecraParser()],\n [\"openapi\", new OpenApiParser()],\n [\"postman\", new PostmanParser()],\n])\n\n/**\n * Auto-detect the parser type based on the input structure\n */\nexport function detectParserType(input: any): ParserType {\n if (!input || typeof input !== \"object\") {\n throw new Error(\"Invalid API spec: input must be an object\")\n }\n\n // Check for Postman Collection\n if (input.info?.schema?.includes(\"v2\")) {\n return \"postman\"\n }\n\n // Check for OpenAPI/Swagger\n if (input.openapi || input.swagger) {\n return \"openapi\"\n }\n\n // Check for Specra format\n if (input.endpoints && Array.isArray(input.endpoints)) {\n return \"specra\"\n }\n\n throw new Error(\n \"Unable to auto-detect API spec format. Supported formats: Specra, OpenAPI 3.x, Postman Collection v2.x\"\n )\n}\n\n/**\n * Parse an API spec using the specified or auto-detected parser\n */\nexport function parseApiSpec(input: any, parserType: ParserType = \"auto\"): SpecraApiSpec {\n // Auto-detect if needed\n const actualType = parserType === \"auto\" ? detectParserType(input) : parserType\n\n // Get the parser\n const parser = parsers.get(actualType)\n if (!parser) {\n throw new Error(`Unknown parser type: ${actualType}`)\n }\n\n // Validate and parse\n if (!parser.validate(input)) {\n throw new Error(`Input does not match ${actualType} format`)\n }\n\n return parser.parse(input)\n}\n\n// Export parsers for direct use\nexport { SpecraParser, OpenApiParser, PostmanParser }\nexport type { ApiSpecParser }\n","import { clsx, type ClassValue } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n\n\n/**\n * Get the correct asset path based on deployment configuration\n * Handles different deployment scenarios:\n * - Vercel/Node.js hosting (standalone build): No basePath needed\n * - GitHub Pages without custom domain: Uses basePath from config\n * - Static hosting with custom domain: No basePath needed\n *\n * @param path - The asset path (can start with or without '/')\n * @returns The properly formatted asset path\n */\nexport function getAssetPath(path: string): string {\n // Get basePath from Next.js config (set during build for static exports)\n const basePath = process.env.NEXT_PUBLIC_BASE_PATH || process.env.__NEXT_ROUTER_BASEPATH || ''\n\n // Normalize the input path: ensure it starts with '/'\n const normalizedPath = path.startsWith('/') ? path : `/${path}`\n\n // If we have a basePath (GitHub Pages without custom domain), prepend it\n if (basePath) {\n // Normalize basePath: remove trailing slash, ensure leading slash\n const normalizedBase = basePath.startsWith('/') ? basePath : `/${basePath}`\n const cleanBase = normalizedBase.replace(/\\/$/, '')\n return `${cleanBase}${normalizedPath}`\n }\n\n // Default: return the normalized path (works for Vercel, custom domains, and dev)\n return normalizedPath\n}","import { getAllDocs, getVersions } from \"./mdx\"\n\nexport interface RedirectMapping {\n from: string\n to: string\n}\n\n/**\n * Build redirect mappings from all docs' redirect_from frontmatter\n */\nexport async function buildRedirectMappings(): Promise<RedirectMapping[]> {\n const versions = getVersions()\n const redirects: RedirectMapping[] = []\n\n for (const version of versions) {\n const docs = await getAllDocs(version)\n \n for (const doc of docs) {\n if (doc.meta.redirect_from && Array.isArray(doc.meta.redirect_from)) {\n for (const oldPath of doc.meta.redirect_from) {\n redirects.push({\n from: oldPath,\n to: `/docs/${version}/${doc.slug}`,\n })\n }\n }\n }\n }\n\n return redirects\n}\n\n/**\n * Find redirect destination for a given path\n */\nexport async function findRedirect(path: string): Promise<string | null> {\n const redirects = await buildRedirectMappings()\n const redirect = redirects.find((r) => r.from === path)\n return redirect ? redirect.to : null\n}\n"],"mappings":"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","names":["import_fs","import_path","path","fs","DOCS_DIR","path","fs","matter","isDevelopment","import_fs","import_path","path","isDevelopment","path","path","path","path","path"]}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildRedirectMappings,
|
|
3
|
+
extractHeadings,
|
|
4
|
+
findRedirect
|
|
5
|
+
} from "../chunk-KTRWWAGL.mjs";
|
|
6
|
+
import {
|
|
7
|
+
OpenApiParser,
|
|
8
|
+
PerfTimer,
|
|
9
|
+
PostmanParser,
|
|
10
|
+
SpecraParser,
|
|
11
|
+
buildSidebarStructure,
|
|
12
|
+
clearAllCaches,
|
|
13
|
+
debugLog,
|
|
14
|
+
detectParserType,
|
|
15
|
+
extractTableOfContents,
|
|
16
|
+
getAdjacentDocs,
|
|
17
|
+
getAllCategoryConfigs,
|
|
18
|
+
getAllDocs,
|
|
19
|
+
getCacheStats,
|
|
20
|
+
getCachedAllDocs,
|
|
21
|
+
getCachedDocBySlug,
|
|
22
|
+
getCachedVersions,
|
|
23
|
+
getCategoryConfig,
|
|
24
|
+
getDocBySlug,
|
|
25
|
+
getVersions,
|
|
26
|
+
isCategoryPage,
|
|
27
|
+
logCacheOperation,
|
|
28
|
+
logFsOperation,
|
|
29
|
+
logMemoryUsage,
|
|
30
|
+
parseApiSpec,
|
|
31
|
+
sortSidebarGroups,
|
|
32
|
+
sortSidebarItems
|
|
33
|
+
} from "../chunk-DR4EPLMT.mjs";
|
|
34
|
+
import {
|
|
35
|
+
cn,
|
|
36
|
+
defaultConfig,
|
|
37
|
+
getAssetPath,
|
|
38
|
+
getConfig,
|
|
39
|
+
getConfigValue,
|
|
40
|
+
initConfig,
|
|
41
|
+
loadConfig,
|
|
42
|
+
processContentWithEnv,
|
|
43
|
+
reloadConfig,
|
|
44
|
+
replaceEnvVariables,
|
|
45
|
+
validateConfig
|
|
46
|
+
} from "../chunk-INL2EC72.mjs";
|
|
47
|
+
export {
|
|
48
|
+
OpenApiParser,
|
|
49
|
+
PerfTimer,
|
|
50
|
+
PostmanParser,
|
|
51
|
+
SpecraParser,
|
|
52
|
+
buildRedirectMappings,
|
|
53
|
+
buildSidebarStructure,
|
|
54
|
+
clearAllCaches,
|
|
55
|
+
cn,
|
|
56
|
+
debugLog,
|
|
57
|
+
defaultConfig,
|
|
58
|
+
detectParserType,
|
|
59
|
+
extractHeadings,
|
|
60
|
+
extractTableOfContents,
|
|
61
|
+
findRedirect,
|
|
62
|
+
getAdjacentDocs,
|
|
63
|
+
getAllCategoryConfigs,
|
|
64
|
+
getAllDocs,
|
|
65
|
+
getAssetPath,
|
|
66
|
+
getCacheStats,
|
|
67
|
+
getCachedAllDocs,
|
|
68
|
+
getCachedDocBySlug,
|
|
69
|
+
getCachedVersions,
|
|
70
|
+
getCategoryConfig,
|
|
71
|
+
getConfig,
|
|
72
|
+
getConfigValue,
|
|
73
|
+
getDocBySlug,
|
|
74
|
+
getVersions,
|
|
75
|
+
initConfig,
|
|
76
|
+
isCategoryPage,
|
|
77
|
+
loadConfig,
|
|
78
|
+
logCacheOperation,
|
|
79
|
+
logFsOperation,
|
|
80
|
+
logMemoryUsage,
|
|
81
|
+
parseApiSpec,
|
|
82
|
+
processContentWithEnv,
|
|
83
|
+
reloadConfig,
|
|
84
|
+
replaceEnvVariables,
|
|
85
|
+
sortSidebarGroups,
|
|
86
|
+
sortSidebarItems,
|
|
87
|
+
validateConfig
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "specra",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A modern documentation library for Next.js with built-in versioning, API reference generation, full-text search, and MDX support",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"require": "./dist/index.cjs"
|
|
12
|
+
},
|
|
13
|
+
"./app/layout": {
|
|
14
|
+
"types": "./dist/app/layout.d.ts",
|
|
15
|
+
"import": "./dist/app/layout.js"
|
|
16
|
+
},
|
|
17
|
+
"./app/docs-page": {
|
|
18
|
+
"types": "./dist/app/docs-page.d.ts",
|
|
19
|
+
"import": "./dist/app/docs-page.js"
|
|
20
|
+
},
|
|
21
|
+
"./app/api/mdx-watch": {
|
|
22
|
+
"types": "./dist/app/api/mdx-watch/route.d.ts",
|
|
23
|
+
"import": "./dist/app/api/mdx-watch/route.js"
|
|
24
|
+
},
|
|
25
|
+
"./components": {
|
|
26
|
+
"types": "./dist/components/index.d.ts",
|
|
27
|
+
"import": "./dist/components/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./lib": {
|
|
30
|
+
"types": "./dist/lib/index.d.ts",
|
|
31
|
+
"import": "./dist/lib/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./styles": "./dist/index.css",
|
|
34
|
+
"./styles.css": "./dist/index.css"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"src",
|
|
39
|
+
"README.md"
|
|
40
|
+
],
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsup",
|
|
43
|
+
"dev": "tsup --watch",
|
|
44
|
+
"typecheck": "tsc --noEmit"
|
|
45
|
+
},
|
|
46
|
+
"keywords": [
|
|
47
|
+
"documentation",
|
|
48
|
+
"docs",
|
|
49
|
+
"nextjs",
|
|
50
|
+
"mdx",
|
|
51
|
+
"api-reference",
|
|
52
|
+
"versioning",
|
|
53
|
+
"search"
|
|
54
|
+
],
|
|
55
|
+
"author": "dalmasonto, arthur-kamau",
|
|
56
|
+
"license": "MIT",
|
|
57
|
+
"repository": {
|
|
58
|
+
"type": "git",
|
|
59
|
+
"url": "https://github.com/yourusername/specra"
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"next": "^14.0.0 || ^15.0.0 || ^16.0.0",
|
|
63
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
64
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
65
|
+
},
|
|
66
|
+
"dependencies": {
|
|
67
|
+
"@hookform/resolvers": "^3.10.0",
|
|
68
|
+
"@radix-ui/react-accordion": "^1.2.2",
|
|
69
|
+
"@radix-ui/react-alert-dialog": "^1.1.15",
|
|
70
|
+
"@radix-ui/react-collapsible": "^1.1.2",
|
|
71
|
+
"@radix-ui/react-dialog": "^1.1.15",
|
|
72
|
+
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
73
|
+
"@radix-ui/react-popover": "^1.1.15",
|
|
74
|
+
"@radix-ui/react-scroll-area": "^1.2.2",
|
|
75
|
+
"@radix-ui/react-select": "^2.1.4",
|
|
76
|
+
"@radix-ui/react-separator": "^1.1.1",
|
|
77
|
+
"@radix-ui/react-slot": "^1.1.1",
|
|
78
|
+
"@radix-ui/react-tabs": "^1.1.13",
|
|
79
|
+
"@radix-ui/react-toast": "^1.2.4",
|
|
80
|
+
"@radix-ui/react-toggle": "^1.1.10",
|
|
81
|
+
"@radix-ui/react-tooltip": "^1.2.1",
|
|
82
|
+
"class-variance-authority": "^0.7.1",
|
|
83
|
+
"clsx": "^2.1.1",
|
|
84
|
+
"cmdk": "^1.0.4",
|
|
85
|
+
"date-fns": "^4.1.0",
|
|
86
|
+
"embla-carousel-react": "^8.5.1",
|
|
87
|
+
"gray-matter": "^4.0.3",
|
|
88
|
+
"katex": "^0.16.27",
|
|
89
|
+
"lucide-react": "^0.454.0",
|
|
90
|
+
"meilisearch": "^0.54.0",
|
|
91
|
+
"mermaid": "^11.12.2",
|
|
92
|
+
"next-mdx-remote": "^5.0.0",
|
|
93
|
+
"next-themes": "^0.4.6",
|
|
94
|
+
"react-hook-form": "^7.60.0",
|
|
95
|
+
"react-markdown": "^9.0.1",
|
|
96
|
+
"recharts": "^2.15.4",
|
|
97
|
+
"rehype-katex": "^7.0.1",
|
|
98
|
+
"rehype-raw": "^7.0.0",
|
|
99
|
+
"rehype-slug": "^6.0.0",
|
|
100
|
+
"remark-gfm": "^4.0.0",
|
|
101
|
+
"remark-math": "^6.0.0",
|
|
102
|
+
"sonner": "^1.7.4",
|
|
103
|
+
"tailwind-merge": "^2.6.0",
|
|
104
|
+
"tailwindcss": "^4.1.18",
|
|
105
|
+
"tailwindcss-animate": "^1.0.7",
|
|
106
|
+
"tw-animate-css": "^1.4.0",
|
|
107
|
+
"zod": "^3.25.76"
|
|
108
|
+
},
|
|
109
|
+
"devDependencies": {
|
|
110
|
+
"@types/node": "^22",
|
|
111
|
+
"@types/react": "^19",
|
|
112
|
+
"@types/react-dom": "^19",
|
|
113
|
+
"next": "^16.1.0",
|
|
114
|
+
"react": "^19.2.3",
|
|
115
|
+
"react-dom": "^19.2.3",
|
|
116
|
+
"tsup": "^8.3.5",
|
|
117
|
+
"typescript": "^5"
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { NextRequest } from 'next/server'
|
|
2
|
+
import { watch } from 'fs'
|
|
3
|
+
import { join } from 'path'
|
|
4
|
+
|
|
5
|
+
export const dynamic = 'force-dynamic'
|
|
6
|
+
export const runtime = 'nodejs'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* API route for watching MDX file changes in development
|
|
10
|
+
* Provides Server-Sent Events (SSE) stream for hot reloading
|
|
11
|
+
* Only available in development mode
|
|
12
|
+
*/
|
|
13
|
+
export async function GET(request: NextRequest) {
|
|
14
|
+
// Only allow in development mode
|
|
15
|
+
if (process.env.NODE_ENV !== 'development') {
|
|
16
|
+
return new Response('Not available in production', { status: 404 })
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const encoder = new TextEncoder()
|
|
20
|
+
|
|
21
|
+
const stream = new ReadableStream({
|
|
22
|
+
start(controller) {
|
|
23
|
+
// Send initial connection message
|
|
24
|
+
const connectMsg = `data: ${JSON.stringify({ type: 'connected' })}\n\n`
|
|
25
|
+
controller.enqueue(encoder.encode(connectMsg))
|
|
26
|
+
|
|
27
|
+
const docsPath = join(process.cwd(), 'docs')
|
|
28
|
+
|
|
29
|
+
// Watch the docs directory recursively
|
|
30
|
+
const watcher = watch(
|
|
31
|
+
docsPath,
|
|
32
|
+
{ recursive: true },
|
|
33
|
+
(eventType, filename) => {
|
|
34
|
+
if (!filename) return
|
|
35
|
+
|
|
36
|
+
// Only watch for .mdx and .json files (MDX files and category configs)
|
|
37
|
+
if (filename.endsWith('.mdx') || filename.endsWith('.json')) {
|
|
38
|
+
console.log(`[MDX Watch] ${eventType}: ${filename}`)
|
|
39
|
+
|
|
40
|
+
const message = `data: ${JSON.stringify({
|
|
41
|
+
type: 'change',
|
|
42
|
+
file: filename,
|
|
43
|
+
eventType
|
|
44
|
+
})}\n\n`
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
controller.enqueue(encoder.encode(message))
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error('[MDX Watch] Error sending message:', error)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
// Handle client disconnect
|
|
56
|
+
request.signal.addEventListener('abort', () => {
|
|
57
|
+
console.log('[MDX Watch] Client disconnected')
|
|
58
|
+
watcher.close()
|
|
59
|
+
try {
|
|
60
|
+
controller.close()
|
|
61
|
+
} catch (error) {
|
|
62
|
+
// Controller might already be closed
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
// Handle errors
|
|
67
|
+
watcher.on('error', (error) => {
|
|
68
|
+
console.error('[MDX Watch] Watcher error:', error)
|
|
69
|
+
watcher.close()
|
|
70
|
+
try {
|
|
71
|
+
controller.close()
|
|
72
|
+
} catch (e) {
|
|
73
|
+
// Controller might already be closed
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
return new Response(stream, {
|
|
80
|
+
headers: {
|
|
81
|
+
'Content-Type': 'text/event-stream',
|
|
82
|
+
'Cache-Control': 'no-cache, no-transform',
|
|
83
|
+
'Connection': 'keep-alive',
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
}
|