@easydocs/dashboard 0.4.2 → 0.5.2
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/next.config.ts +1 -15
- package/package.json +11 -6
- package/src/components/SchemaViewer.tsx +2 -2
- package/src/components/SpecEditor.tsx +5 -10
- package/src/lib/db.ts +2 -42
- package/tsconfig.json +24 -6
- package/src/lib/operation-schema.ts +0 -44
package/next.config.ts
CHANGED
|
@@ -3,21 +3,7 @@ import type { NextConfig } from 'next'
|
|
|
3
3
|
const config: NextConfig = {
|
|
4
4
|
transpilePackages: ['@easydocs/core'],
|
|
5
5
|
serverExternalPackages: ['@libsql/client', 'libsql', 'better-sqlite3'],
|
|
6
|
-
|
|
7
|
-
if (isServer) {
|
|
8
|
-
const existing = Array.isArray(webpackConfig.externals) ? webpackConfig.externals : []
|
|
9
|
-
webpackConfig.externals = [
|
|
10
|
-
...existing,
|
|
11
|
-
({ request }: { request?: string }, callback: (err?: Error | null, result?: string) => void) => {
|
|
12
|
-
if (request && /^(libsql|@libsql\/)/.test(request)) {
|
|
13
|
-
return callback(null, `commonjs ${request}`)
|
|
14
|
-
}
|
|
15
|
-
callback()
|
|
16
|
-
},
|
|
17
|
-
]
|
|
18
|
-
}
|
|
19
|
-
return webpackConfig
|
|
20
|
-
},
|
|
6
|
+
turbopack: {},
|
|
21
7
|
}
|
|
22
8
|
|
|
23
9
|
export default config
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@easydocs/dashboard",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "EasyDocs docs dashboard — view and export AI-generated OpenAPI specs",
|
|
5
5
|
"files": [
|
|
6
6
|
"src",
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
"@codemirror/theme-one-dark": "^6.1.3",
|
|
16
16
|
"@uiw/react-codemirror": "^4.25.9",
|
|
17
17
|
"js-yaml": "^4.1.0",
|
|
18
|
-
"next": "
|
|
18
|
+
"next": "16.2.6",
|
|
19
19
|
"react": "^19.0.0",
|
|
20
|
-
"react-dom": "^19.
|
|
20
|
+
"react-dom": "^19.2.6",
|
|
21
21
|
"zod": "^3.25.76",
|
|
22
|
-
"@easydocs/core": "0.
|
|
22
|
+
"@easydocs/core": "0.5.2"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/js-yaml": "^4.0.9",
|
|
@@ -27,16 +27,21 @@
|
|
|
27
27
|
"@types/react": "^19",
|
|
28
28
|
"@types/react-dom": "^19",
|
|
29
29
|
"eslint": "^9",
|
|
30
|
-
"eslint-config-next": "
|
|
30
|
+
"eslint-config-next": "16.2.6",
|
|
31
31
|
"postcss": "^8",
|
|
32
32
|
"tailwindcss": "^3.4.1",
|
|
33
33
|
"typescript": "^5"
|
|
34
34
|
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/RubenGlez/easydocs",
|
|
38
|
+
"directory": ""
|
|
39
|
+
},
|
|
35
40
|
"scripts": {
|
|
36
41
|
"dev": "next dev --port 4999",
|
|
37
42
|
"build": "next build",
|
|
38
43
|
"start": "next start --port 4999",
|
|
39
44
|
"typecheck": "tsc --noEmit",
|
|
40
|
-
"lint": "
|
|
45
|
+
"lint": "eslint ."
|
|
41
46
|
}
|
|
42
47
|
}
|
|
@@ -9,7 +9,7 @@ function ValueNode({ value, depth = 0 }: { value: unknown; depth?: number }) {
|
|
|
9
9
|
if (typeof value === 'number')
|
|
10
10
|
return <span className="text-amber-400">{String(value)}</span>
|
|
11
11
|
if (typeof value === 'string')
|
|
12
|
-
return <span className="text-green-400"
|
|
12
|
+
return <span className="text-green-400">"{value}"</span>
|
|
13
13
|
|
|
14
14
|
if (Array.isArray(value)) {
|
|
15
15
|
if (value.length === 0) return <span className="text-zinc-400">[]</span>
|
|
@@ -38,7 +38,7 @@ function ValueNode({ value, depth = 0 }: { value: unknown; depth?: number }) {
|
|
|
38
38
|
<div className="ml-4">
|
|
39
39
|
{entries.map(([k, v], i) => (
|
|
40
40
|
<div key={k}>
|
|
41
|
-
<span className="text-sky-300"
|
|
41
|
+
<span className="text-sky-300">"{k}"</span>
|
|
42
42
|
<span className="text-zinc-400">: </span>
|
|
43
43
|
<ValueNode value={v} depth={depth + 1} />
|
|
44
44
|
{i < entries.length - 1 && <span className="text-zinc-600">,</span>}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useState
|
|
3
|
+
import { useState } from 'react'
|
|
4
4
|
import CodeMirror from '@uiw/react-codemirror'
|
|
5
5
|
import { json } from '@codemirror/lang-json'
|
|
6
6
|
import { oneDark } from '@codemirror/theme-one-dark'
|
|
7
7
|
import type { Endpoint } from '@easydocs/core/schema'
|
|
8
|
-
import type { Operation } from '@easydocs/core'
|
|
9
|
-
import { OperationSchema } from '
|
|
8
|
+
import type { Operation } from '@easydocs/core/spec/schema'
|
|
9
|
+
import { OperationSchema } from '@easydocs/core/spec/schema'
|
|
10
10
|
|
|
11
11
|
interface Props {
|
|
12
12
|
endpoint: Endpoint
|
|
@@ -32,17 +32,12 @@ function validate(raw: string): string | null {
|
|
|
32
32
|
export function SpecEditor({ endpoint, onSaved, onCancel }: Props) {
|
|
33
33
|
const activeSpec = endpoint.isManuallyEdited ? endpoint.manualSpec : endpoint.spec
|
|
34
34
|
const [value, setValue] = useState(() => JSON.stringify(activeSpec, null, 2))
|
|
35
|
-
const
|
|
35
|
+
const validationError = validate(value)
|
|
36
36
|
const [saveError, setSaveError] = useState<string | null>(null)
|
|
37
37
|
const [saving, setSaving] = useState(false)
|
|
38
38
|
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
setValidationError(validate(value))
|
|
41
|
-
}, [value])
|
|
42
|
-
|
|
43
39
|
async function handleSave() {
|
|
44
|
-
|
|
45
|
-
if (err) { setValidationError(err); return }
|
|
40
|
+
if (validationError) return
|
|
46
41
|
setSaveError(null)
|
|
47
42
|
setSaving(true)
|
|
48
43
|
const parsed = JSON.parse(value) as Operation
|
package/src/lib/db.ts
CHANGED
|
@@ -4,7 +4,9 @@ import {
|
|
|
4
4
|
getEndpointsByProject,
|
|
5
5
|
getAllProjects,
|
|
6
6
|
findOrCreateProject,
|
|
7
|
+
buildFullSpec,
|
|
7
8
|
} from '@easydocs/core'
|
|
9
|
+
export { buildFullSpec }
|
|
8
10
|
import type { Endpoint, Project } from '@easydocs/core/schema'
|
|
9
11
|
|
|
10
12
|
let db: ReturnType<typeof createDB> | null = null
|
|
@@ -24,45 +26,3 @@ export async function fetchEndpoints(projectSlug?: string): Promise<Endpoint[]>
|
|
|
24
26
|
const projectId = await findOrCreateProject(db, projectSlug)
|
|
25
27
|
return getEndpointsByProject(db, projectId)
|
|
26
28
|
}
|
|
27
|
-
|
|
28
|
-
const SECURITY_SCHEME_DEFS: Record<string, unknown> = {
|
|
29
|
-
bearerAuth: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' },
|
|
30
|
-
basicAuth: { type: 'http', scheme: 'basic' },
|
|
31
|
-
apiKeyHeader: { type: 'apiKey', in: 'header', name: 'X-API-Key' },
|
|
32
|
-
apiKeyQuery: { type: 'apiKey', in: 'query', name: 'api_key' },
|
|
33
|
-
cookieAuth: { type: 'apiKey', in: 'cookie', name: 'session' },
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function buildFullSpec(endpointList: Endpoint[], projectName?: string) {
|
|
37
|
-
const usedSchemes = new Set<string>()
|
|
38
|
-
const paths: Record<string, Record<string, unknown>> = {}
|
|
39
|
-
|
|
40
|
-
for (const e of endpointList) {
|
|
41
|
-
if (!e.path || !e.method) continue
|
|
42
|
-
const activeSpec = e.isManuallyEdited && e.manualSpec ? e.manualSpec : e.spec
|
|
43
|
-
if (!activeSpec) continue
|
|
44
|
-
|
|
45
|
-
if (!paths[e.path]) paths[e.path] = {}
|
|
46
|
-
paths[e.path][e.method.toLowerCase()] = activeSpec
|
|
47
|
-
|
|
48
|
-
if (activeSpec.security) {
|
|
49
|
-
for (const entry of activeSpec.security) {
|
|
50
|
-
for (const name of Object.keys(entry)) {
|
|
51
|
-
if (SECURITY_SCHEME_DEFS[name]) usedSchemes.add(name)
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const securitySchemes =
|
|
58
|
-
usedSchemes.size > 0
|
|
59
|
-
? Object.fromEntries([...usedSchemes].map((n) => [n, SECURITY_SCHEME_DEFS[n]]))
|
|
60
|
-
: undefined
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
openapi: '3.0.3',
|
|
64
|
-
info: { title: projectName ?? 'API Documentation', version: '1.0.0' },
|
|
65
|
-
paths,
|
|
66
|
-
...(securitySchemes ? { components: { securitySchemes } } : {}),
|
|
67
|
-
}
|
|
68
|
-
}
|
package/tsconfig.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
3
|
"target": "ES2017",
|
|
4
|
-
"lib": [
|
|
4
|
+
"lib": [
|
|
5
|
+
"dom",
|
|
6
|
+
"dom.iterable",
|
|
7
|
+
"esnext"
|
|
8
|
+
],
|
|
5
9
|
"allowJs": true,
|
|
6
10
|
"skipLibCheck": true,
|
|
7
11
|
"strict": true,
|
|
@@ -11,13 +15,27 @@
|
|
|
11
15
|
"moduleResolution": "bundler",
|
|
12
16
|
"resolveJsonModule": true,
|
|
13
17
|
"isolatedModules": true,
|
|
14
|
-
"jsx": "
|
|
18
|
+
"jsx": "react-jsx",
|
|
15
19
|
"incremental": true,
|
|
16
|
-
"plugins": [
|
|
20
|
+
"plugins": [
|
|
21
|
+
{
|
|
22
|
+
"name": "next"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
17
25
|
"paths": {
|
|
18
|
-
"@/*": [
|
|
26
|
+
"@/*": [
|
|
27
|
+
"./src/*"
|
|
28
|
+
]
|
|
19
29
|
}
|
|
20
30
|
},
|
|
21
|
-
"include": [
|
|
22
|
-
|
|
31
|
+
"include": [
|
|
32
|
+
"next-env.d.ts",
|
|
33
|
+
"**/*.ts",
|
|
34
|
+
"**/*.tsx",
|
|
35
|
+
".next/types/**/*.ts",
|
|
36
|
+
".next/dev/types/**/*.ts"
|
|
37
|
+
],
|
|
38
|
+
"exclude": [
|
|
39
|
+
"node_modules"
|
|
40
|
+
]
|
|
23
41
|
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
|
|
3
|
-
const SchemaObject = z.record(z.any()).optional()
|
|
4
|
-
|
|
5
|
-
const MediaTypeSchema = z.object({
|
|
6
|
-
schema: SchemaObject,
|
|
7
|
-
example: z.any().optional(),
|
|
8
|
-
examples: z.record(z.any()).optional(),
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
const ParameterSchema = z.object({
|
|
12
|
-
name: z.string(),
|
|
13
|
-
in: z.enum(['query', 'header', 'path', 'cookie']),
|
|
14
|
-
description: z.string().optional(),
|
|
15
|
-
required: z.boolean().default(false),
|
|
16
|
-
deprecated: z.boolean().optional(),
|
|
17
|
-
schema: SchemaObject,
|
|
18
|
-
example: z.any().optional(),
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
const ResponseSchema = z.object({
|
|
22
|
-
description: z.string(),
|
|
23
|
-
headers: z.record(z.any()).optional(),
|
|
24
|
-
content: z.record(MediaTypeSchema).optional(),
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
export const OperationSchema = z.object({
|
|
28
|
-
tags: z.array(z.string()).optional(),
|
|
29
|
-
summary: z.string().optional(),
|
|
30
|
-
description: z.string().optional(),
|
|
31
|
-
operationId: z.string().optional(),
|
|
32
|
-
parameters: z.array(ParameterSchema).optional(),
|
|
33
|
-
requestBody: z
|
|
34
|
-
.object({
|
|
35
|
-
description: z.string().optional(),
|
|
36
|
-
required: z.boolean().optional(),
|
|
37
|
-
content: z.record(MediaTypeSchema),
|
|
38
|
-
})
|
|
39
|
-
.nullable()
|
|
40
|
-
.optional(),
|
|
41
|
-
responses: z.record(ResponseSchema).default({}),
|
|
42
|
-
deprecated: z.boolean().optional(),
|
|
43
|
-
security: z.array(z.record(z.array(z.string()))).optional(),
|
|
44
|
-
})
|