@clidey/dory 0.27.0 → 0.30.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/bin/dist/dory.js +115 -29
- package/bin/dist/dory.js.map +1 -1
- package/package.json +12 -4
- package/.dockerignore +0 -4
- package/CLAUDE.md +0 -178
- package/CODE_OF_CONDUCT.md +0 -128
- package/CONTRIBUTING.md +0 -91
- package/SECURITY.md +0 -11
- package/bin/dory.ts +0 -433
- package/dist/favicon.svg +0 -75
- package/dist/frontmatter.json +0 -77
- package/dist/index.html +0 -55
- package/dist/llms.txt +0 -561
- package/dist/logo/logo.svg +0 -18
- package/docs/api-playground-demo.mdx +0 -119
- package/docs/api-reference/asyncapi.json +0 -324
- package/docs/api-reference/openapi.json +0 -366
- package/docs/api-reference/operations/create.mdx +0 -5
- package/docs/api-reference/operations/delete.mdx +0 -5
- package/docs/api-reference/operations/get.mdx +0 -5
- package/docs/api-reference/operations/update.mdx +0 -5
- package/docs/api-reference/websocket-operations/receive-chat-message.mdx +0 -5
- package/docs/api-reference/websocket-operations/receive-notifications.mdx +0 -5
- package/docs/api-reference/websocket-operations/receive-presence-update.mdx +0 -5
- package/docs/api-reference/websocket-operations/send-chat-message.mdx +0 -5
- package/docs/api-reference/websocket-operations/send-presence-update.mdx +0 -5
- package/docs/dory.json +0 -64
- package/docs/favicon.svg +0 -75
- package/docs/features.mdx +0 -440
- package/docs/introduction.mdx +0 -44
- package/docs/logo/logo.svg +0 -18
- package/docs/websocket-playground-demo.mdx +0 -135
- package/index.html +0 -54
- package/scripts/generate-frontmatter-json.js +0 -154
- package/scripts/generate-llm-txt.js +0 -115
- package/src/app.css +0 -5
- package/src/app.tsx +0 -13
- package/src/components/dropdown.tsx +0 -53
- package/src/components/hooks.tsx +0 -28
- package/src/components/loading.tsx +0 -21
- package/src/components/mdx-provider.tsx +0 -13
- package/src/components/navigation.tsx +0 -58
- package/src/components/notification.tsx +0 -13
- package/src/components/routes.tsx +0 -92
- package/src/components/search-index.ts +0 -43
- package/src/components/search.tsx +0 -435
- package/src/components/store.ts +0 -216
- package/src/config/mdx.js +0 -12
- package/src/config/mdx.ts +0 -12
- package/src/main.tsx +0 -22
- package/src/mdx/accordion.tsx +0 -41
- package/src/mdx/api-playground.tsx +0 -659
- package/src/mdx/api.tsx +0 -354
- package/src/mdx/async-api.tsx +0 -353
- package/src/mdx/callout.tsx +0 -64
- package/src/mdx/card.tsx +0 -54
- package/src/mdx/code.tsx +0 -289
- package/src/mdx/expandable.tsx +0 -36
- package/src/mdx/fence.tsx +0 -74
- package/src/mdx/icon.tsx +0 -73
- package/src/mdx/latex.tsx +0 -26
- package/src/mdx/list.tsx +0 -7
- package/src/mdx/mdx.tsx +0 -47
- package/src/mdx/mermaid.tsx +0 -147
- package/src/mdx/open-api.tsx +0 -311
- package/src/mdx/processor.ts +0 -174
- package/src/mdx/source.tsx +0 -58
- package/src/mdx/step.tsx +0 -51
- package/src/mdx/table.tsx +0 -20
- package/src/mdx/tag.tsx +0 -53
- package/src/mdx/text.tsx +0 -99
- package/src/mdx/websocket-playground.tsx +0 -824
- package/src/plugins/docs-assets.ts +0 -79
- package/src/plugins/frontmatter-dev-server.ts +0 -158
- package/src/plugins/frontmatter-generator.ts +0 -157
- package/src/plugins/llm-txt-dev-server.ts +0 -183
- package/src/plugins/llm-txt-generator.ts +0 -186
- package/src/plugins/sanitize.ts +0 -163
- package/src/plugins/unified-mdx-plugin.ts +0 -35
- package/src/theme.scss +0 -72
- package/src/types/config.ts +0 -47
- package/src/ui/header.tsx +0 -91
- package/src/ui/layout.tsx +0 -193
- package/src/ui/mobile-navigation.tsx +0 -64
- package/src/ui/prev-next-link.tsx +0 -74
- package/src/ui/table-of-content.tsx +0 -166
- package/src/utils/colors.ts +0 -54
- package/src/utils/fonts.ts +0 -23
- package/src/utils/functions.ts +0 -12
- package/src/vite-env.d.ts +0 -1
- package/tsconfig.app.json +0 -33
- package/tsconfig.bin.json +0 -24
- package/tsconfig.json +0 -7
- package/tsconfig.node.json +0 -26
- package/vite.config.ts +0 -41
package/src/mdx/mermaid.tsx
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline'
|
|
2
|
-
import classNames from 'classnames'
|
|
3
|
-
import { useEffect, useMemo, useRef, useState } from 'preact/compat'
|
|
4
|
-
|
|
5
|
-
function fixNodeLabelsWithSpecialChars(content: string): string {
|
|
6
|
-
// Pattern to match node definitions with brackets containing special characters
|
|
7
|
-
// Matches: NodeId[text content] where text content has special chars and isn't already quoted
|
|
8
|
-
const nodePattern = /(\w+)\[([^\]"]*[(),\s][^\]"]*)\]/g;
|
|
9
|
-
|
|
10
|
-
return content.replace(nodePattern, (match, nodeId, labelContent) => {
|
|
11
|
-
// Only wrap in quotes if not already quoted and contains special characters
|
|
12
|
-
if (!labelContent.startsWith('"') || !labelContent.endsWith('"')) {
|
|
13
|
-
return `${nodeId}["${labelContent}"]`;
|
|
14
|
-
}
|
|
15
|
-
return match;
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function MermaidRenderer({ content }: { content: string }) {
|
|
20
|
-
const mermaidRef = useRef<HTMLDivElement>(null)
|
|
21
|
-
const [isFullscreen, setIsFullscreen] = useState(false)
|
|
22
|
-
const [isLoaded, setIsLoaded] = useState(false)
|
|
23
|
-
const [error, setError] = useState<string | null>(null)
|
|
24
|
-
const [isValid, setIsValid] = useState<boolean | null>(null)
|
|
25
|
-
|
|
26
|
-
const diagramId = useMemo(() => `mermaid-${btoa(content).replace(/[^a-zA-Z0-9]/g, '').substring(0, 10)}`, [content])
|
|
27
|
-
|
|
28
|
-
useEffect(() => {
|
|
29
|
-
let mounted = true
|
|
30
|
-
|
|
31
|
-
const renderMermaid = async () => {
|
|
32
|
-
try {
|
|
33
|
-
const mermaid = (await import('mermaid')).default
|
|
34
|
-
|
|
35
|
-
if (!mounted || !mermaidRef.current) return
|
|
36
|
-
|
|
37
|
-
mermaid.initialize({
|
|
38
|
-
startOnLoad: true,
|
|
39
|
-
theme: 'base',
|
|
40
|
-
themeVariables: {
|
|
41
|
-
fontSize: '14px',
|
|
42
|
-
nodePadding: 20,
|
|
43
|
-
notePadding: 15,
|
|
44
|
-
padding: 10,
|
|
45
|
-
nodeBorderRadius: 8,
|
|
46
|
-
},
|
|
47
|
-
flowchart: {
|
|
48
|
-
nodeSpacing: 50,
|
|
49
|
-
rankSpacing: 80,
|
|
50
|
-
padding: 20,
|
|
51
|
-
useMaxWidth: true,
|
|
52
|
-
},
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
if (mermaidRef.current) {
|
|
56
|
-
mermaidRef.current.innerHTML = ''
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const contentWithoutStyle = content
|
|
60
|
-
.replace(/^\s*style .*$/gm, '')
|
|
61
|
-
.replace(/^\s*classDef .*$/gm, '')
|
|
62
|
-
.replace(/^\s*class .*$/gm, '');
|
|
63
|
-
|
|
64
|
-
// Fix node labels with special characters by wrapping them in quotes
|
|
65
|
-
const processedContent = fixNodeLabelsWithSpecialChars(contentWithoutStyle);
|
|
66
|
-
|
|
67
|
-
// Validate the mermaid syntax first
|
|
68
|
-
try {
|
|
69
|
-
await mermaid.parse(processedContent)
|
|
70
|
-
setIsValid(true)
|
|
71
|
-
} catch (parseError) {
|
|
72
|
-
setIsValid(false)
|
|
73
|
-
if (mounted) {
|
|
74
|
-
setError(parseError instanceof Error ? parseError.message : 'Invalid diagram syntax')
|
|
75
|
-
}
|
|
76
|
-
return
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const { svg } = await mermaid.render(diagramId, processedContent);
|
|
80
|
-
|
|
81
|
-
if (mounted && mermaidRef.current) {
|
|
82
|
-
mermaidRef.current.innerHTML = svg
|
|
83
|
-
setIsLoaded(true)
|
|
84
|
-
}
|
|
85
|
-
} catch (err) {
|
|
86
|
-
console.error('Mermaid rendering error:', err)
|
|
87
|
-
if (mounted) {
|
|
88
|
-
setIsValid(false)
|
|
89
|
-
setError(err instanceof Error ? err.message : 'Failed to render diagram')
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
renderMermaid()
|
|
95
|
-
|
|
96
|
-
return () => {
|
|
97
|
-
mounted = false
|
|
98
|
-
}
|
|
99
|
-
}, [content, diagramId])
|
|
100
|
-
|
|
101
|
-
const toggleFullscreen = () => {
|
|
102
|
-
setIsFullscreen(!isFullscreen)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (error || isValid === false) {
|
|
106
|
-
return (
|
|
107
|
-
<div className="mermaid-wrapper my-8">
|
|
108
|
-
<div className="flex flex-col items-center justify-center py-12 px-6 text-center bg-gradient-to-br from-red-50 to-red-100 dark:from-red-900/20 dark:to-red-800/20 border-2 border-dashed border-red-300 dark:border-red-700 rounded-lg">
|
|
109
|
-
<div className="w-16 h-16 mb-4 flex items-center justify-center bg-red-100 dark:bg-red-900/30 rounded-full">
|
|
110
|
-
<ExclamationTriangleIcon className="w-8 h-8 text-red-500" />
|
|
111
|
-
</div>
|
|
112
|
-
<h3 className="text-lg font-semibold text-red-700 dark:text-red-300 mb-2">
|
|
113
|
-
Diagram Syntax Error
|
|
114
|
-
</h3>
|
|
115
|
-
<p className="text-red-600 dark:text-red-400 mb-4 max-w-md">
|
|
116
|
-
The Mermaid diagram contains invalid syntax and cannot be rendered.
|
|
117
|
-
</p>
|
|
118
|
-
<div className="bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-800 rounded-md p-3 max-w-md">
|
|
119
|
-
<p className="text-sm text-red-700 dark:text-red-300 font-mono break-all">
|
|
120
|
-
{error}
|
|
121
|
-
</p>
|
|
122
|
-
</div>
|
|
123
|
-
</div>
|
|
124
|
-
</div>
|
|
125
|
-
)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return (
|
|
129
|
-
<div className="mermaid-wrapper my-8">
|
|
130
|
-
{!isLoaded && (
|
|
131
|
-
<div className="flex items-center justify-center py-8 text-gray-400">
|
|
132
|
-
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
|
|
133
|
-
<span className="ml-2">Loading diagram...</span>
|
|
134
|
-
</div>
|
|
135
|
-
)}
|
|
136
|
-
<div
|
|
137
|
-
ref={mermaidRef}
|
|
138
|
-
className={classNames('mermaid cursor-pointer transition-all duration-300 rounded-lg p-4 bg-white', {
|
|
139
|
-
'fixed inset-0 z-50 bg-black/90 flex items-center justify-center backdrop-blur-sm': isFullscreen,
|
|
140
|
-
'opacity-0': !isLoaded,
|
|
141
|
-
'opacity-100': isLoaded,
|
|
142
|
-
})}
|
|
143
|
-
onClick={toggleFullscreen}
|
|
144
|
-
/>
|
|
145
|
-
</div>
|
|
146
|
-
)
|
|
147
|
-
}
|
package/src/mdx/open-api.tsx
DELETED
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
import { useState } from "preact/hooks"
|
|
2
|
-
import { APIGroup } from "./api"
|
|
3
|
-
import { APIPlayground, PlayButton, type Server } from "./api-playground"
|
|
4
|
-
import { Fence } from "./fence"
|
|
5
|
-
import { Tag } from "./tag"
|
|
6
|
-
import { Col, Row } from "./text"
|
|
7
|
-
import { Sheet, SheetContent, SheetHeader, SheetTitle, Button } from "@clidey/ux"
|
|
8
|
-
|
|
9
|
-
export function Properties({ children }: { children: React.ReactNode }) {
|
|
10
|
-
return (
|
|
11
|
-
<div className="my-2">
|
|
12
|
-
<ul
|
|
13
|
-
role="list"
|
|
14
|
-
className="m-0 max-w-[calc(var(--container-lg)-(--spacing(8)))] list-none divide-y divide-zinc-900/5 p-0 dark:divide-white/10"
|
|
15
|
-
>
|
|
16
|
-
{children}
|
|
17
|
-
</ul>
|
|
18
|
-
</div>
|
|
19
|
-
)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function Property({
|
|
23
|
-
name,
|
|
24
|
-
children,
|
|
25
|
-
type,
|
|
26
|
-
required = false,
|
|
27
|
-
}: {
|
|
28
|
-
name: string
|
|
29
|
-
children: React.ReactNode
|
|
30
|
-
type?: string
|
|
31
|
-
required?: boolean
|
|
32
|
-
}) {
|
|
33
|
-
return (
|
|
34
|
-
<div className="m-0 px-0 first:pt-0 last:pb-0">
|
|
35
|
-
<div className="my-4 flex flex-wrap items-center gap-x-3 gap-2">
|
|
36
|
-
<div className="flex gap-2 items-center grow">
|
|
37
|
-
<Tag className="px-4 py-1">{name}</Tag>
|
|
38
|
-
<p className="font-mono text-xs text-zinc-400 dark:text-zinc-500">
|
|
39
|
-
{type}
|
|
40
|
-
</p>
|
|
41
|
-
<div className="flex grow justify-end ">
|
|
42
|
-
{required && <span className="text-rose-400 ml-1 text-xs">Required</span>}
|
|
43
|
-
</div>
|
|
44
|
-
</div>
|
|
45
|
-
<p className="w-full flex-none [&>:first-child]:mt-0 [&>:last-child]:mb-0 text-xs text-muted-foreground">
|
|
46
|
-
{children}
|
|
47
|
-
</p>
|
|
48
|
-
</div>
|
|
49
|
-
</div>
|
|
50
|
-
)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
interface ParamFieldProps {
|
|
54
|
-
body: string;
|
|
55
|
-
type: string;
|
|
56
|
-
default?: string | number;
|
|
57
|
-
required?: boolean;
|
|
58
|
-
children: React.ReactNode;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function ParamField({ body, type, default: defaultValue, required, children }: ParamFieldProps) {
|
|
62
|
-
return (
|
|
63
|
-
<Property name={body} type={type} required={required}>
|
|
64
|
-
{children}
|
|
65
|
-
{defaultValue != null && (
|
|
66
|
-
<span className="text-gray-500 ml-1">(default: {defaultValue})</span>
|
|
67
|
-
)}
|
|
68
|
-
</Property>
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
interface ResponseFieldProps {
|
|
73
|
-
name: string;
|
|
74
|
-
type: string;
|
|
75
|
-
required?: boolean;
|
|
76
|
-
children: React.ReactNode;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function ResponseField({ name, type, required, children }: ResponseFieldProps) {
|
|
80
|
-
return (
|
|
81
|
-
<Property name={name} type={type} required={required}>
|
|
82
|
-
{children}
|
|
83
|
-
</Property>
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
interface Schema {
|
|
88
|
-
$ref?: string;
|
|
89
|
-
type?: string;
|
|
90
|
-
items?: Schema;
|
|
91
|
-
properties?: Record<string, Schema>;
|
|
92
|
-
required?: boolean;
|
|
93
|
-
description?: string;
|
|
94
|
-
default?: string | number;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
interface Components {
|
|
98
|
-
schemas?: Record<string, Schema>;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
interface Parameter {
|
|
102
|
-
name: string;
|
|
103
|
-
schema?: Schema;
|
|
104
|
-
required?: boolean;
|
|
105
|
-
description?: string;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
interface Content {
|
|
109
|
-
schema?: Schema;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
interface RequestBody {
|
|
113
|
-
description?: string;
|
|
114
|
-
content?: {
|
|
115
|
-
'application/json'?: Content;
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
interface Response {
|
|
120
|
-
content?: {
|
|
121
|
-
'application/json'?: Content;
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
interface PathMethod {
|
|
126
|
-
description?: string;
|
|
127
|
-
parameters?: Parameter[];
|
|
128
|
-
requestBody?: RequestBody;
|
|
129
|
-
responses: Record<string, Response>;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export interface OpenAPIJson {
|
|
133
|
-
paths: Record<string, Record<string, PathMethod>>;
|
|
134
|
-
components: Components;
|
|
135
|
-
servers?: Server[];
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function resolveSchema(schema: Schema | undefined, components: Components): Schema {
|
|
139
|
-
if (!schema) return {};
|
|
140
|
-
|
|
141
|
-
if (schema.$ref) {
|
|
142
|
-
const refPath = schema.$ref.replace(/^#\/components\/schemas\//, '');
|
|
143
|
-
return resolveSchema(components.schemas?.[refPath], components);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (schema.type === 'array' && schema.items) {
|
|
147
|
-
return {
|
|
148
|
-
...schema,
|
|
149
|
-
items: resolveSchema(schema.items, components)
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (schema.properties) {
|
|
154
|
-
const resolvedProps: Record<string, Schema> = {};
|
|
155
|
-
for (const [key, value] of Object.entries(schema.properties)) {
|
|
156
|
-
resolvedProps[key] = resolveSchema(value, components);
|
|
157
|
-
}
|
|
158
|
-
return {
|
|
159
|
-
...schema,
|
|
160
|
-
properties: resolvedProps
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return schema;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
export function OpenAPI({ openAPIJson, method: selectedMethod, path: selectedPath }: { openAPIJson: OpenAPIJson, method: string, path: string }) {
|
|
168
|
-
const { paths, components } = openAPIJson;
|
|
169
|
-
const [showPlayground, setShowPlayground] = useState(false);
|
|
170
|
-
|
|
171
|
-
const handleShowPlayground = () => {
|
|
172
|
-
setShowPlayground(true);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return (
|
|
176
|
-
<div className="max-w-none w-full">
|
|
177
|
-
<Sheet open={showPlayground} onOpenChange={setShowPlayground} modal={true}>
|
|
178
|
-
<SheetContent side="bottom" className="h-[90vh] w-full overflow-y-auto p-8">
|
|
179
|
-
<SheetHeader className="p-0">
|
|
180
|
-
<SheetTitle>API Playground</SheetTitle>
|
|
181
|
-
</SheetHeader>
|
|
182
|
-
<APIPlayground
|
|
183
|
-
method={selectedMethod}
|
|
184
|
-
url={selectedPath}
|
|
185
|
-
servers={openAPIJson.servers}
|
|
186
|
-
/>
|
|
187
|
-
</SheetContent>
|
|
188
|
-
</Sheet>
|
|
189
|
-
{Object.entries(paths).map(([path, methods]) => {
|
|
190
|
-
if (path !== selectedPath) {
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return (
|
|
195
|
-
<div key={path} className="my-8">
|
|
196
|
-
{Object.entries(methods).map(([method, details]) => {
|
|
197
|
-
const { description, parameters, requestBody, responses } = details;
|
|
198
|
-
const methodUpper = method.toUpperCase();
|
|
199
|
-
|
|
200
|
-
if (methodUpper !== selectedMethod) {
|
|
201
|
-
return null;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return (
|
|
205
|
-
<Row key={`${method}-${path}`} cols={2} className="gap-0">
|
|
206
|
-
<Col>
|
|
207
|
-
<h2 className="flex items-center mt-4">
|
|
208
|
-
<Tag variant="medium" className="mr-2 py-1 px-4 rounded-lg">{methodUpper}</Tag>
|
|
209
|
-
<span className="ml-2 font-mono">{path}</span>
|
|
210
|
-
</h2>
|
|
211
|
-
|
|
212
|
-
{description && <p>{description}</p>}
|
|
213
|
-
|
|
214
|
-
{parameters && parameters.length > 0 && (
|
|
215
|
-
<div className="mt-4">
|
|
216
|
-
<h3 className="font-bold">Parameters</h3>
|
|
217
|
-
<Properties>
|
|
218
|
-
{parameters.map((param) => (
|
|
219
|
-
<ParamField
|
|
220
|
-
key={param.name}
|
|
221
|
-
body={param.name}
|
|
222
|
-
type={param.schema?.type ?? 'string'}
|
|
223
|
-
required={param.required}
|
|
224
|
-
default={param.schema?.default}
|
|
225
|
-
>
|
|
226
|
-
{param.description}
|
|
227
|
-
</ParamField>
|
|
228
|
-
))}
|
|
229
|
-
</Properties>
|
|
230
|
-
</div>
|
|
231
|
-
)}
|
|
232
|
-
{requestBody && (
|
|
233
|
-
<div className="mt-4">
|
|
234
|
-
<h3 className="font-bold">Request Body</h3>
|
|
235
|
-
<p>{requestBody.description}</p>
|
|
236
|
-
{requestBody.content?.['application/json']?.schema && (
|
|
237
|
-
(() => {
|
|
238
|
-
const resolvedSchema = resolveSchema(requestBody.content['application/json'].schema, components);
|
|
239
|
-
return (
|
|
240
|
-
<Properties>
|
|
241
|
-
{Object.entries(resolvedSchema.properties || {}).map(([propName, prop]) => (
|
|
242
|
-
<ParamField
|
|
243
|
-
key={propName}
|
|
244
|
-
body={propName}
|
|
245
|
-
type={prop.type ?? 'string'}
|
|
246
|
-
required={prop.required}
|
|
247
|
-
default={prop.default}
|
|
248
|
-
>
|
|
249
|
-
{prop.description}
|
|
250
|
-
</ParamField>
|
|
251
|
-
))}
|
|
252
|
-
</Properties>
|
|
253
|
-
);
|
|
254
|
-
})()
|
|
255
|
-
)}
|
|
256
|
-
</div>
|
|
257
|
-
)}
|
|
258
|
-
{components?.schemas && (
|
|
259
|
-
<div>
|
|
260
|
-
<h3 className="font-bold">Schemas</h3>
|
|
261
|
-
{Object.entries(components.schemas).filter(([, schema]) => schema.properties).map(([name, schema]) => (
|
|
262
|
-
<div key={name} className="mt-8">
|
|
263
|
-
<h4>{name}</h4>
|
|
264
|
-
<Properties>
|
|
265
|
-
{Object.entries(schema.properties || {}).map(([propName, prop]) => (
|
|
266
|
-
<ResponseField
|
|
267
|
-
key={propName}
|
|
268
|
-
name={propName}
|
|
269
|
-
type={prop.type ?? 'string'}
|
|
270
|
-
required={prop.required}
|
|
271
|
-
>
|
|
272
|
-
{prop.description}
|
|
273
|
-
</ResponseField>
|
|
274
|
-
))}
|
|
275
|
-
</Properties>
|
|
276
|
-
</div>
|
|
277
|
-
))}
|
|
278
|
-
</div>
|
|
279
|
-
)}
|
|
280
|
-
</Col>
|
|
281
|
-
<Col sticky>
|
|
282
|
-
<Button
|
|
283
|
-
onClick={handleShowPlayground}
|
|
284
|
-
className='ml-auto mt-3 flex items-center gap-2'>
|
|
285
|
-
Try
|
|
286
|
-
<PlayButton className="h-4 w-4" />
|
|
287
|
-
</Button>
|
|
288
|
-
<APIGroup title="Request" tag={methodUpper} label={path}>
|
|
289
|
-
{Object.entries(responses).map(([code, response]) => (
|
|
290
|
-
<div key={code} title={code}>
|
|
291
|
-
{response.content?.['application/json']?.schema && (() => {
|
|
292
|
-
const resolvedSchema = resolveSchema(response.content['application/json'].schema, components);
|
|
293
|
-
return (
|
|
294
|
-
<Fence language="json">
|
|
295
|
-
{JSON.stringify(resolvedSchema, null, 2)}
|
|
296
|
-
</Fence>
|
|
297
|
-
);
|
|
298
|
-
})()}
|
|
299
|
-
</div>
|
|
300
|
-
))}
|
|
301
|
-
</APIGroup>
|
|
302
|
-
</Col>
|
|
303
|
-
</Row>
|
|
304
|
-
)
|
|
305
|
-
})}
|
|
306
|
-
</div>
|
|
307
|
-
)
|
|
308
|
-
})}
|
|
309
|
-
</div>
|
|
310
|
-
)
|
|
311
|
-
}
|
package/src/mdx/processor.ts
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import { compile } from '@mdx-js/mdx';
|
|
2
|
-
import { preprocessMdxTags } from '../plugins/sanitize';
|
|
3
|
-
import { getMdxConfig } from '../config/mdx';
|
|
4
|
-
|
|
5
|
-
export interface ProcessMdxOptions {
|
|
6
|
-
isDevelopment?: boolean;
|
|
7
|
-
fileName?: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* SINGLE SOURCE OF TRUTH for MDX preprocessing.
|
|
12
|
-
*
|
|
13
|
-
* This processor is used by:
|
|
14
|
-
* - CLI verify:content command (bin/dory.ts) - full processing + compilation
|
|
15
|
-
* - Vite build via unified-mdx-plugin.ts - preprocessing only
|
|
16
|
-
*
|
|
17
|
-
* ALL MDX processing steps should be added here to ensure consistency.
|
|
18
|
-
*/
|
|
19
|
-
export async function preprocessMdxContent(
|
|
20
|
-
content: string,
|
|
21
|
-
fileName: string = 'content.mdx'
|
|
22
|
-
): Promise<{
|
|
23
|
-
success: boolean;
|
|
24
|
-
processedContent?: string;
|
|
25
|
-
error?: Error;
|
|
26
|
-
}> {
|
|
27
|
-
try {
|
|
28
|
-
// Step 1: Apply preprocessor (handles MDX tags, code blocks, etc.)
|
|
29
|
-
const preprocessor = preprocessMdxTags();
|
|
30
|
-
let processedContent = content;
|
|
31
|
-
|
|
32
|
-
if (preprocessor.transform) {
|
|
33
|
-
const result = preprocessor.transform(content, fileName);
|
|
34
|
-
if (result && typeof result === 'object' && result.code) {
|
|
35
|
-
processedContent = result.code;
|
|
36
|
-
} else if (typeof result === 'string') {
|
|
37
|
-
processedContent = result;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Step 2: Convert URLs in angle brackets to markdown links
|
|
42
|
-
// Handles cases like <https://example.com> which MDX would interpret as JSX
|
|
43
|
-
// But preserves them inside code blocks
|
|
44
|
-
|
|
45
|
-
// Process line by line to handle code blocks correctly
|
|
46
|
-
const lines = processedContent.split('\n');
|
|
47
|
-
let inCodeBlock = false;
|
|
48
|
-
|
|
49
|
-
processedContent = lines.map(line => {
|
|
50
|
-
// Check if we're entering or leaving a code block (with optional indentation)
|
|
51
|
-
if (line.trim().startsWith('```')) {
|
|
52
|
-
inCodeBlock = !inCodeBlock;
|
|
53
|
-
return line;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Don't process lines inside code blocks
|
|
57
|
-
if (inCodeBlock) {
|
|
58
|
-
return line;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// For lines outside code blocks, preserve inline code
|
|
62
|
-
// More robust regex that handles inline code with potential spaces
|
|
63
|
-
// Matches: `...` including multi-line content
|
|
64
|
-
let result = '';
|
|
65
|
-
let lastIndex = 0;
|
|
66
|
-
|
|
67
|
-
// Find all inline code spans in the line
|
|
68
|
-
const inlineCodeRegex = /`([^`]+)`/g;
|
|
69
|
-
let match;
|
|
70
|
-
|
|
71
|
-
while ((match = inlineCodeRegex.exec(line)) !== null) {
|
|
72
|
-
// Process text before the inline code
|
|
73
|
-
const beforeCode = line.substring(lastIndex, match.index);
|
|
74
|
-
result += beforeCode.replace(/<(https?:\/\/[^\s>]+)>/g, '[$1]($1)');
|
|
75
|
-
|
|
76
|
-
// Keep inline code as-is
|
|
77
|
-
result += match[0];
|
|
78
|
-
|
|
79
|
-
lastIndex = match.index + match[0].length;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Process remaining text after last inline code (or entire line if no inline code)
|
|
83
|
-
const remainingText = line.substring(lastIndex);
|
|
84
|
-
result += remainingText.replace(/<(https?:\/\/[^\s>]+)>/g, '[$1]($1)');
|
|
85
|
-
|
|
86
|
-
return result;
|
|
87
|
-
}).join('\n');
|
|
88
|
-
|
|
89
|
-
// Step 3: Add any future preprocessing steps here
|
|
90
|
-
// Example: processedContent = await yourNewPreprocessor(processedContent);
|
|
91
|
-
// Example: processedContent = removeHTMLComments(processedContent);
|
|
92
|
-
// Example: processedContent = expandCustomShortcodes(processedContent);
|
|
93
|
-
// ALL preprocessing added here will automatically apply to:
|
|
94
|
-
// - CLI commands (verify:content)
|
|
95
|
-
// - Vite build (pnpm run build)
|
|
96
|
-
// - Vite dev server (pnpm run dev)
|
|
97
|
-
|
|
98
|
-
return {
|
|
99
|
-
success: true,
|
|
100
|
-
processedContent
|
|
101
|
-
};
|
|
102
|
-
} catch (error) {
|
|
103
|
-
return {
|
|
104
|
-
success: false,
|
|
105
|
-
error: error as Error
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Process and compile MDX content through the full pipeline.
|
|
112
|
-
* Used by CLI commands that need the compiled output.
|
|
113
|
-
*/
|
|
114
|
-
export async function processMdxContent(
|
|
115
|
-
content: string,
|
|
116
|
-
options: ProcessMdxOptions = {}
|
|
117
|
-
): Promise<{
|
|
118
|
-
success: boolean;
|
|
119
|
-
compiled?: any;
|
|
120
|
-
error?: Error;
|
|
121
|
-
processedContent?: string;
|
|
122
|
-
}> {
|
|
123
|
-
const { isDevelopment = false, fileName = 'content.mdx' } = options;
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
// Step 1: Preprocess
|
|
127
|
-
const preprocessResult = await preprocessMdxContent(content, fileName);
|
|
128
|
-
if (!preprocessResult.success) {
|
|
129
|
-
return {
|
|
130
|
-
success: false,
|
|
131
|
-
error: preprocessResult.error
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const processedContent = preprocessResult.processedContent || content;
|
|
136
|
-
|
|
137
|
-
// Step 2: Compile MDX with the same config as main build
|
|
138
|
-
const mdxConfig = getMdxConfig(isDevelopment);
|
|
139
|
-
const compiled = await compile(processedContent, {
|
|
140
|
-
...mdxConfig,
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
return {
|
|
144
|
-
success: true,
|
|
145
|
-
compiled,
|
|
146
|
-
processedContent
|
|
147
|
-
};
|
|
148
|
-
} catch (error) {
|
|
149
|
-
return {
|
|
150
|
-
success: false,
|
|
151
|
-
error: error as Error
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Verify MDX content can be compiled without errors.
|
|
158
|
-
* Uses the same processing pipeline as processMdxContent.
|
|
159
|
-
*/
|
|
160
|
-
export async function verifyMdxContent(
|
|
161
|
-
content: string,
|
|
162
|
-
fileName?: string
|
|
163
|
-
): Promise<{ valid: boolean; error?: Error }> {
|
|
164
|
-
const result = await processMdxContent(content, { fileName });
|
|
165
|
-
|
|
166
|
-
if (result.success) {
|
|
167
|
-
return { valid: true };
|
|
168
|
-
} else {
|
|
169
|
-
return {
|
|
170
|
-
valid: false,
|
|
171
|
-
error: result.error
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
}
|
package/src/mdx/source.tsx
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
type PathRange = {
|
|
2
|
-
path: string;
|
|
3
|
-
range?: string;
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
type SourceProps = {
|
|
7
|
-
url: string;
|
|
8
|
-
branch?: string;
|
|
9
|
-
paths: PathRange[];
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const Source = ({ url, branch = "main", paths }: SourceProps) => {
|
|
13
|
-
const createSourceUrl = (path: string, range?: string) => {
|
|
14
|
-
if (!range) {
|
|
15
|
-
if (url.includes('github.com')) {
|
|
16
|
-
return `${url}/blob/${branch}/${path}`;
|
|
17
|
-
} else if (url.includes('bitbucket.org')) {
|
|
18
|
-
return `${url}/src/${branch}/${path}`;
|
|
19
|
-
} else if (url.includes('gitlab.com')) {
|
|
20
|
-
return `${url}/-/blob/${branch}/${path}`;
|
|
21
|
-
}
|
|
22
|
-
return url;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const [start, end] = range.split('-');
|
|
26
|
-
|
|
27
|
-
if (url.includes('github.com')) {
|
|
28
|
-
return `${url}/blob/${branch}/${path}#L${start}${end ? `-L${end}` : ''}`;
|
|
29
|
-
} else if (url.includes('bitbucket.org')) {
|
|
30
|
-
return `${url}/src/${branch}/${path}#lines-${start}${end ? `:${end}` : ''}`;
|
|
31
|
-
} else if (url.includes('gitlab.com')) {
|
|
32
|
-
return `${url}/-/blob/${branch}/${path}#L${range.replace('-', '-')}`;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return url;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
return (
|
|
39
|
-
<div>
|
|
40
|
-
<p className="text-sm text-slate-900 dark:text-white">Sources:</p>
|
|
41
|
-
<ul className="list-none pl-0 flex flex-wrap gap-2 mt-2">
|
|
42
|
-
{paths.map(({ path, range }) => (
|
|
43
|
-
<li key={path + (range || '')}>
|
|
44
|
-
<a
|
|
45
|
-
href={createSourceUrl(path, range)}
|
|
46
|
-
target="_blank"
|
|
47
|
-
rel="noopener noreferrer"
|
|
48
|
-
className="rounded px-3 py-1 font-mono text-sm text-slate-700 dark:text-slate-400 hover:opacity-80 transition-colors border border-slate-200 dark:border-slate-700"
|
|
49
|
-
>
|
|
50
|
-
{path}
|
|
51
|
-
{range && <span className="ml-2 opacity-70">{range}</span>}
|
|
52
|
-
</a>
|
|
53
|
-
</li>
|
|
54
|
-
))}
|
|
55
|
-
</ul>
|
|
56
|
-
</div>
|
|
57
|
-
);
|
|
58
|
-
};
|