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,143 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
|
5
|
+
import { XIcon } from "lucide-react"
|
|
6
|
+
|
|
7
|
+
import { cn } from "@/lib/utils"
|
|
8
|
+
|
|
9
|
+
function Dialog({
|
|
10
|
+
...props
|
|
11
|
+
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
|
12
|
+
return <DialogPrimitive.Root data-slot="dialog" {...props} />
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function DialogTrigger({
|
|
16
|
+
...props
|
|
17
|
+
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
|
18
|
+
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function DialogPortal({
|
|
22
|
+
...props
|
|
23
|
+
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
|
24
|
+
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function DialogClose({
|
|
28
|
+
...props
|
|
29
|
+
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
|
30
|
+
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function DialogOverlay({
|
|
34
|
+
className,
|
|
35
|
+
...props
|
|
36
|
+
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
|
37
|
+
return (
|
|
38
|
+
<DialogPrimitive.Overlay
|
|
39
|
+
data-slot="dialog-overlay"
|
|
40
|
+
className={cn(
|
|
41
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50 dark:bg-black/70 backdrop-blur-md",
|
|
42
|
+
className
|
|
43
|
+
)}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function DialogContent({
|
|
50
|
+
className,
|
|
51
|
+
children,
|
|
52
|
+
showCloseButton = true,
|
|
53
|
+
...props
|
|
54
|
+
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
|
55
|
+
showCloseButton?: boolean
|
|
56
|
+
}) {
|
|
57
|
+
return (
|
|
58
|
+
<DialogPortal data-slot="dialog-portal">
|
|
59
|
+
<DialogOverlay />
|
|
60
|
+
<DialogPrimitive.Content
|
|
61
|
+
data-slot="dialog-content"
|
|
62
|
+
className={cn(
|
|
63
|
+
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg",
|
|
64
|
+
className
|
|
65
|
+
)}
|
|
66
|
+
{...props}
|
|
67
|
+
>
|
|
68
|
+
{children}
|
|
69
|
+
{showCloseButton && (
|
|
70
|
+
<DialogPrimitive.Close
|
|
71
|
+
data-slot="dialog-close"
|
|
72
|
+
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
|
73
|
+
>
|
|
74
|
+
<XIcon />
|
|
75
|
+
<span className="sr-only">Close</span>
|
|
76
|
+
</DialogPrimitive.Close>
|
|
77
|
+
)}
|
|
78
|
+
</DialogPrimitive.Content>
|
|
79
|
+
</DialogPortal>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
84
|
+
return (
|
|
85
|
+
<div
|
|
86
|
+
data-slot="dialog-header"
|
|
87
|
+
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
|
88
|
+
{...props}
|
|
89
|
+
/>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
94
|
+
return (
|
|
95
|
+
<div
|
|
96
|
+
data-slot="dialog-footer"
|
|
97
|
+
className={cn(
|
|
98
|
+
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
99
|
+
className
|
|
100
|
+
)}
|
|
101
|
+
{...props}
|
|
102
|
+
/>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function DialogTitle({
|
|
107
|
+
className,
|
|
108
|
+
...props
|
|
109
|
+
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
|
110
|
+
return (
|
|
111
|
+
<DialogPrimitive.Title
|
|
112
|
+
data-slot="dialog-title"
|
|
113
|
+
className={cn("text-lg leading-none font-semibold", className)}
|
|
114
|
+
{...props}
|
|
115
|
+
/>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function DialogDescription({
|
|
120
|
+
className,
|
|
121
|
+
...props
|
|
122
|
+
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
|
123
|
+
return (
|
|
124
|
+
<DialogPrimitive.Description
|
|
125
|
+
data-slot="dialog-description"
|
|
126
|
+
className={cn("text-muted-foreground text-sm", className)}
|
|
127
|
+
{...props}
|
|
128
|
+
/>
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export {
|
|
133
|
+
Dialog,
|
|
134
|
+
DialogClose,
|
|
135
|
+
DialogContent,
|
|
136
|
+
DialogDescription,
|
|
137
|
+
DialogFooter,
|
|
138
|
+
DialogHeader,
|
|
139
|
+
DialogOverlay,
|
|
140
|
+
DialogPortal,
|
|
141
|
+
DialogTitle,
|
|
142
|
+
DialogTrigger,
|
|
143
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
|
6
|
+
return (
|
|
7
|
+
<input
|
|
8
|
+
type={type}
|
|
9
|
+
data-slot="input"
|
|
10
|
+
className={cn(
|
|
11
|
+
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
12
|
+
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
13
|
+
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
14
|
+
className
|
|
15
|
+
)}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { Input }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
|
6
|
+
return (
|
|
7
|
+
<textarea
|
|
8
|
+
data-slot="textarea"
|
|
9
|
+
className={cn(
|
|
10
|
+
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
11
|
+
className
|
|
12
|
+
)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { Textarea }
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Specra Documentation SDK
|
|
3
|
+
*
|
|
4
|
+
* A modern documentation framework for Next.js with:
|
|
5
|
+
* - MDX-based documentation
|
|
6
|
+
* - Versioning support
|
|
7
|
+
* - API reference generation (OpenAPI, Postman, Specra formats)
|
|
8
|
+
* - Full-text search integration (MeiliSearch)
|
|
9
|
+
* - Tab groups for organizing content
|
|
10
|
+
* - Dark mode and theming
|
|
11
|
+
* - SEO optimization
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// Core library functions
|
|
15
|
+
export * from './lib'
|
|
16
|
+
|
|
17
|
+
// React components
|
|
18
|
+
export * from './components'
|
|
19
|
+
|
|
20
|
+
// Next.js app exports (for re-exporting in user projects)
|
|
21
|
+
export { default as RootLayout, generateMetadata as generateRootMetadata } from './app/layout'
|
|
22
|
+
export {
|
|
23
|
+
default as DocsPage,
|
|
24
|
+
generateMetadata as generateDocsMetadata,
|
|
25
|
+
generateStaticParams as generateDocsStaticParams
|
|
26
|
+
} from './app/docs-page'
|
|
27
|
+
|
|
28
|
+
// Types
|
|
29
|
+
export type { SpecraConfig, SiteConfig, NavigationConfig, ThemeConfig } from './lib/config.types'
|
|
30
|
+
export type { Doc, DocMeta, TocItem } from './lib/mdx'
|
|
31
|
+
export type {
|
|
32
|
+
ApiDocumentation,
|
|
33
|
+
ApiSpecConfig,
|
|
34
|
+
ParsedApiSpec,
|
|
35
|
+
RestEndpoint,
|
|
36
|
+
ApiParameter,
|
|
37
|
+
ApiResponse,
|
|
38
|
+
GraphQLSchema,
|
|
39
|
+
WebSocketConnection
|
|
40
|
+
} from './lib/api.types'
|
|
41
|
+
// Note: SpecraApiResponse is exported from './lib' as a renamed type to avoid conflicts
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple API Documentation Format for Specra
|
|
3
|
+
* Easy to write, easy to parse
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface ApiParam {
|
|
7
|
+
name: string
|
|
8
|
+
type: string
|
|
9
|
+
required?: boolean
|
|
10
|
+
description?: string
|
|
11
|
+
default?: any
|
|
12
|
+
example?: any
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ApiHeader {
|
|
16
|
+
name: string
|
|
17
|
+
value: string
|
|
18
|
+
description?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ApiResponse {
|
|
22
|
+
status: number
|
|
23
|
+
description?: string
|
|
24
|
+
example?: any
|
|
25
|
+
schema?: any
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ApiEndpointSpec {
|
|
29
|
+
title: string
|
|
30
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE"
|
|
31
|
+
path: string
|
|
32
|
+
description?: string
|
|
33
|
+
|
|
34
|
+
// Parameters
|
|
35
|
+
pathParams?: ApiParam[]
|
|
36
|
+
queryParams?: ApiParam[]
|
|
37
|
+
headers?: ApiHeader[]
|
|
38
|
+
body?: {
|
|
39
|
+
description?: string
|
|
40
|
+
example?: any
|
|
41
|
+
schema?: any
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Responses
|
|
45
|
+
successResponse?: ApiResponse
|
|
46
|
+
errorResponses?: ApiResponse[]
|
|
47
|
+
|
|
48
|
+
// Examples
|
|
49
|
+
examples?: {
|
|
50
|
+
title: string
|
|
51
|
+
language: string
|
|
52
|
+
code: string
|
|
53
|
+
}[]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface SpecraApiSpec {
|
|
57
|
+
version?: string
|
|
58
|
+
title?: string
|
|
59
|
+
description?: string
|
|
60
|
+
baseUrl: string
|
|
61
|
+
|
|
62
|
+
// Environment variables for interpolation
|
|
63
|
+
env?: Record<string, string>
|
|
64
|
+
|
|
65
|
+
// Global headers applied to all endpoints
|
|
66
|
+
globalHeaders?: ApiHeader[]
|
|
67
|
+
|
|
68
|
+
// Authentication
|
|
69
|
+
auth?: {
|
|
70
|
+
type: "bearer" | "apiKey" | "basic"
|
|
71
|
+
description?: string
|
|
72
|
+
headerName?: string
|
|
73
|
+
tokenPrefix?: string
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Endpoints
|
|
77
|
+
endpoints: ApiEndpointSpec[]
|
|
78
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Specra API Documentation Schema
|
|
3
|
+
* Supports REST, GraphQL, and WebSocket APIs
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Common Types
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS"
|
|
11
|
+
|
|
12
|
+
export interface ApiParameter {
|
|
13
|
+
name: string
|
|
14
|
+
type: string
|
|
15
|
+
required?: boolean
|
|
16
|
+
description?: string
|
|
17
|
+
default?: any
|
|
18
|
+
example?: any
|
|
19
|
+
enum?: string[]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ApiResponse {
|
|
23
|
+
status: number
|
|
24
|
+
description: string
|
|
25
|
+
schema?: any
|
|
26
|
+
example?: any
|
|
27
|
+
headers?: Record<string, string>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ApiExample {
|
|
31
|
+
title: string
|
|
32
|
+
language: string
|
|
33
|
+
code: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface ApiAuthentication {
|
|
37
|
+
type: "apiKey" | "bearer" | "basic" | "oauth2" | "none"
|
|
38
|
+
description?: string
|
|
39
|
+
location?: "header" | "query" | "cookie" // For apiKey
|
|
40
|
+
name?: string // Header/query parameter name
|
|
41
|
+
scheme?: string // Bearer scheme
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ============================================================================
|
|
45
|
+
// REST API Types
|
|
46
|
+
// ============================================================================
|
|
47
|
+
|
|
48
|
+
export interface RestEndpoint {
|
|
49
|
+
type: "rest"
|
|
50
|
+
method: HttpMethod
|
|
51
|
+
path: string
|
|
52
|
+
summary: string
|
|
53
|
+
description?: string
|
|
54
|
+
operationId?: string
|
|
55
|
+
tags?: string[]
|
|
56
|
+
deprecated?: boolean
|
|
57
|
+
|
|
58
|
+
// Authentication
|
|
59
|
+
authentication?: ApiAuthentication
|
|
60
|
+
|
|
61
|
+
// Parameters
|
|
62
|
+
pathParams?: ApiParameter[]
|
|
63
|
+
queryParams?: ApiParameter[]
|
|
64
|
+
headers?: ApiParameter[]
|
|
65
|
+
body?: {
|
|
66
|
+
contentType: string
|
|
67
|
+
schema?: any
|
|
68
|
+
example?: any
|
|
69
|
+
description?: string
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Responses
|
|
73
|
+
responses: ApiResponse[]
|
|
74
|
+
|
|
75
|
+
// Code Examples
|
|
76
|
+
examples?: ApiExample[]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ============================================================================
|
|
80
|
+
// GraphQL API Types
|
|
81
|
+
// ============================================================================
|
|
82
|
+
|
|
83
|
+
export interface GraphQLField {
|
|
84
|
+
name: string
|
|
85
|
+
type: string
|
|
86
|
+
description?: string
|
|
87
|
+
args?: ApiParameter[]
|
|
88
|
+
deprecated?: boolean
|
|
89
|
+
deprecationReason?: string
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface GraphQLType {
|
|
93
|
+
name: string
|
|
94
|
+
kind: "OBJECT" | "INPUT_OBJECT" | "ENUM" | "SCALAR" | "INTERFACE" | "UNION"
|
|
95
|
+
description?: string
|
|
96
|
+
fields?: GraphQLField[]
|
|
97
|
+
enumValues?: { name: string; description?: string }[]
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface GraphQLQuery {
|
|
101
|
+
type: "graphql"
|
|
102
|
+
operationType: "query" | "mutation" | "subscription"
|
|
103
|
+
name: string
|
|
104
|
+
description?: string
|
|
105
|
+
args?: ApiParameter[]
|
|
106
|
+
returnType: string
|
|
107
|
+
example?: string
|
|
108
|
+
response?: any
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface GraphQLSchema {
|
|
112
|
+
queries?: GraphQLQuery[]
|
|
113
|
+
mutations?: GraphQLQuery[]
|
|
114
|
+
subscriptions?: GraphQLQuery[]
|
|
115
|
+
types?: GraphQLType[]
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ============================================================================
|
|
119
|
+
// WebSocket API Types
|
|
120
|
+
// ============================================================================
|
|
121
|
+
|
|
122
|
+
export interface WebSocketEvent {
|
|
123
|
+
type: "websocket"
|
|
124
|
+
event: string
|
|
125
|
+
direction: "client-to-server" | "server-to-client" | "bidirectional"
|
|
126
|
+
description?: string
|
|
127
|
+
payload?: {
|
|
128
|
+
schema?: any
|
|
129
|
+
example?: any
|
|
130
|
+
}
|
|
131
|
+
response?: {
|
|
132
|
+
schema?: any
|
|
133
|
+
example?: any
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface WebSocketConnection {
|
|
138
|
+
url: string
|
|
139
|
+
authentication?: ApiAuthentication
|
|
140
|
+
description?: string
|
|
141
|
+
events: WebSocketEvent[]
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ============================================================================
|
|
145
|
+
// Unified API Documentation
|
|
146
|
+
// ============================================================================
|
|
147
|
+
|
|
148
|
+
export interface ApiDocumentation {
|
|
149
|
+
version: string
|
|
150
|
+
title: string
|
|
151
|
+
description?: string
|
|
152
|
+
baseUrl?: string
|
|
153
|
+
servers?: Array<{
|
|
154
|
+
url: string
|
|
155
|
+
description?: string
|
|
156
|
+
}>
|
|
157
|
+
|
|
158
|
+
// REST endpoints
|
|
159
|
+
rest?: {
|
|
160
|
+
endpoints: RestEndpoint[]
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// GraphQL schema
|
|
164
|
+
graphql?: GraphQLSchema
|
|
165
|
+
|
|
166
|
+
// WebSocket connections
|
|
167
|
+
websocket?: WebSocketConnection[]
|
|
168
|
+
|
|
169
|
+
// Global settings
|
|
170
|
+
authentication?: ApiAuthentication[]
|
|
171
|
+
headers?: Record<string, string>
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ============================================================================
|
|
175
|
+
// API Spec Sources
|
|
176
|
+
// ============================================================================
|
|
177
|
+
|
|
178
|
+
export interface ApiSpecConfig {
|
|
179
|
+
// Source type
|
|
180
|
+
source: "openapi" | "postman" | "insomnia" | "specra" | "manual"
|
|
181
|
+
|
|
182
|
+
// File path (relative to public/api-specs/)
|
|
183
|
+
path?: string
|
|
184
|
+
|
|
185
|
+
// Inline spec (for Specra format)
|
|
186
|
+
spec?: ApiDocumentation
|
|
187
|
+
|
|
188
|
+
// Auto-generate pages
|
|
189
|
+
autoGenerate?: boolean
|
|
190
|
+
|
|
191
|
+
// Output directory for generated pages
|
|
192
|
+
outputDir?: string
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ============================================================================
|
|
196
|
+
// Helper Types for Parsing
|
|
197
|
+
// ============================================================================
|
|
198
|
+
|
|
199
|
+
export interface ParsedApiSpec {
|
|
200
|
+
source: ApiSpecConfig["source"]
|
|
201
|
+
documentation: ApiDocumentation
|
|
202
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import fs from "fs"
|
|
2
|
+
import path from "path"
|
|
3
|
+
|
|
4
|
+
export interface CategoryConfig {
|
|
5
|
+
label?: string
|
|
6
|
+
position?: number
|
|
7
|
+
sidebar_position?: number // Alternative naming for position
|
|
8
|
+
link?: {
|
|
9
|
+
type: "generated-index" | "doc"
|
|
10
|
+
slug?: string
|
|
11
|
+
}
|
|
12
|
+
collapsed?: boolean
|
|
13
|
+
collapsible?: boolean
|
|
14
|
+
icon?: string // Icon name for sidebar display (Lucide icon name)
|
|
15
|
+
tab_group?: string // Tab group ID for organizing categories into tabs
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const DOCS_DIR = path.join(process.cwd(), "docs")
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Read category.json from a folder
|
|
22
|
+
*/
|
|
23
|
+
export function getCategoryConfig(folderPath: string): CategoryConfig | null {
|
|
24
|
+
try {
|
|
25
|
+
const categoryPath = path.join(folderPath, "_category_.json")
|
|
26
|
+
|
|
27
|
+
if (!fs.existsSync(categoryPath)) {
|
|
28
|
+
return null
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const content = fs.readFileSync(categoryPath, "utf8")
|
|
32
|
+
return JSON.parse(content) as CategoryConfig
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error(`Error reading category config from ${folderPath}:`, error)
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get all category configs for a version
|
|
41
|
+
*/
|
|
42
|
+
export function getAllCategoryConfigs(version: string): Map<string, CategoryConfig> {
|
|
43
|
+
const configs = new Map<string, CategoryConfig>()
|
|
44
|
+
const versionDir = path.join(DOCS_DIR, version)
|
|
45
|
+
|
|
46
|
+
if (!fs.existsSync(versionDir)) {
|
|
47
|
+
return configs
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function scanDirectory(dir: string, relativePath: string = "") {
|
|
51
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true })
|
|
52
|
+
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
if (entry.isDirectory()) {
|
|
55
|
+
const fullPath = path.join(dir, entry.name)
|
|
56
|
+
const relPath = relativePath ? `${relativePath}/${entry.name}` : entry.name
|
|
57
|
+
|
|
58
|
+
const config = getCategoryConfig(fullPath)
|
|
59
|
+
if (config) {
|
|
60
|
+
configs.set(relPath, config)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Recursively scan subdirectories
|
|
64
|
+
scanDirectory(fullPath, relPath)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
scanDirectory(versionDir)
|
|
70
|
+
return configs
|
|
71
|
+
}
|