fumadocs-openapi 3.3.0 → 4.0.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/dist/chunk-RSFOKBAM.js +115 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.js +232 -25
- package/dist/playground-D8pYn13F.d.ts +52 -0
- package/dist/playground-XE3Y6DRJ.js +963 -0
- package/dist/ui/index.d.ts +46 -0
- package/dist/ui/index.js +197 -0
- package/package.json +38 -3
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// src/ui/shared.ts
|
|
2
|
+
import { cva } from "class-variance-authority";
|
|
3
|
+
var badgeVariants = cva(
|
|
4
|
+
"rounded border px-1.5 py-1 text-xs font-medium leading-[12px]",
|
|
5
|
+
{
|
|
6
|
+
variants: {
|
|
7
|
+
color: {
|
|
8
|
+
green: "border-green-400/50 bg-green-400/20 text-green-600 dark:text-green-400",
|
|
9
|
+
yellow: "border-yellow-400/50 bg-yellow-400/20 text-yellow-600 dark:text-yellow-400",
|
|
10
|
+
red: "border-red-400/50 bg-red-400/20 text-red-600 dark:text-red-400",
|
|
11
|
+
blue: "border-blue-400/50 bg-blue-400/20 text-blue-600 dark:text-blue-400",
|
|
12
|
+
orange: "border-orange-400/50 bg-orange-400/20 text-orange-600 dark:text-orange-400"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
);
|
|
17
|
+
function getBadgeColor(method) {
|
|
18
|
+
switch (method) {
|
|
19
|
+
case "PUT":
|
|
20
|
+
return "yellow";
|
|
21
|
+
case "PATCH":
|
|
22
|
+
return "orange";
|
|
23
|
+
case "POST":
|
|
24
|
+
return "blue";
|
|
25
|
+
case "DELETE":
|
|
26
|
+
return "red";
|
|
27
|
+
default:
|
|
28
|
+
return "green";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function getDefaultValue(item, references) {
|
|
32
|
+
if (item.type === "object")
|
|
33
|
+
return Object.fromEntries(
|
|
34
|
+
Object.entries(item.properties).map(([key, prop]) => [
|
|
35
|
+
key,
|
|
36
|
+
getDefaultValue(references[prop.schema], references)
|
|
37
|
+
])
|
|
38
|
+
);
|
|
39
|
+
if (item.type === "array") return [];
|
|
40
|
+
if (item.type === "null") return null;
|
|
41
|
+
if (item.type === "switcher")
|
|
42
|
+
return getDefaultValue(
|
|
43
|
+
resolve(Object.values(item.items)[0], references),
|
|
44
|
+
references
|
|
45
|
+
);
|
|
46
|
+
return String(item.defaultValue);
|
|
47
|
+
}
|
|
48
|
+
function getDefaultValues(field, context) {
|
|
49
|
+
return Object.fromEntries(
|
|
50
|
+
field.map((p) => [p.name, getDefaultValue(p, context)])
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
function resolve(schema, references) {
|
|
54
|
+
if (typeof schema === "string") return references[schema];
|
|
55
|
+
if (schema.type !== "ref") return schema;
|
|
56
|
+
return {
|
|
57
|
+
...references[schema.schema],
|
|
58
|
+
description: schema.description,
|
|
59
|
+
isRequired: schema.isRequired
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/ui/contexts/api.tsx
|
|
64
|
+
import { createContext, useContext, useEffect, useState } from "react";
|
|
65
|
+
import { jsx } from "react/jsx-runtime";
|
|
66
|
+
var ApiContext = createContext({
|
|
67
|
+
baseUrl: void 0,
|
|
68
|
+
setBaseUrl: () => void 0,
|
|
69
|
+
highlighter: null
|
|
70
|
+
});
|
|
71
|
+
function useApiContext() {
|
|
72
|
+
return useContext(ApiContext);
|
|
73
|
+
}
|
|
74
|
+
async function initHighlighter() {
|
|
75
|
+
const { createHighlighterCore } = await import("shiki/core");
|
|
76
|
+
const getWasm = await import("shiki/wasm");
|
|
77
|
+
return createHighlighterCore({
|
|
78
|
+
themes: [
|
|
79
|
+
import("shiki/themes/github-light.mjs"),
|
|
80
|
+
import("shiki/themes/github-dark.mjs")
|
|
81
|
+
],
|
|
82
|
+
langs: [import("shiki/langs/json.mjs")],
|
|
83
|
+
loadWasm: getWasm
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
var highlighterInstance;
|
|
87
|
+
function ApiProvider({
|
|
88
|
+
defaultBaseUrl,
|
|
89
|
+
children
|
|
90
|
+
}) {
|
|
91
|
+
const [highlighter, setHighlighter] = useState(null);
|
|
92
|
+
const [baseUrl, setBaseUrl] = useState(defaultBaseUrl);
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
setBaseUrl((prev) => localStorage.getItem("apiBaseUrl") ?? prev);
|
|
95
|
+
if (highlighterInstance) setHighlighter(highlighterInstance);
|
|
96
|
+
else
|
|
97
|
+
void initHighlighter().then((res) => {
|
|
98
|
+
setHighlighter(res);
|
|
99
|
+
});
|
|
100
|
+
}, []);
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (baseUrl) localStorage.setItem("apiBaseUrl", baseUrl);
|
|
103
|
+
}, [baseUrl]);
|
|
104
|
+
return /* @__PURE__ */ jsx(ApiContext.Provider, { value: { baseUrl, setBaseUrl, highlighter }, children });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export {
|
|
108
|
+
badgeVariants,
|
|
109
|
+
getBadgeColor,
|
|
110
|
+
getDefaultValue,
|
|
111
|
+
getDefaultValues,
|
|
112
|
+
resolve,
|
|
113
|
+
useApiContext,
|
|
114
|
+
ApiProvider
|
|
115
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { OpenAPIV3 } from 'openapi-types';
|
|
2
|
+
import { A as APIPlaygroundProps } from './playground-D8pYn13F.js';
|
|
3
|
+
export { P as PrimitiveRequestField, a as ReferenceSchema, R as RequestSchema } from './playground-D8pYn13F.js';
|
|
2
4
|
|
|
3
5
|
interface ResponsesProps {
|
|
4
6
|
items: string[];
|
|
@@ -24,8 +26,11 @@ interface RequestProps {
|
|
|
24
26
|
name: string;
|
|
25
27
|
code: string;
|
|
26
28
|
}
|
|
29
|
+
interface RootProps {
|
|
30
|
+
baseUrl?: string;
|
|
31
|
+
}
|
|
27
32
|
interface Renderer {
|
|
28
|
-
Root: (child: string[]) => string;
|
|
33
|
+
Root: (props: RootProps, child: string[]) => string;
|
|
29
34
|
API: (child: string[]) => string;
|
|
30
35
|
APIInfo: (props: APIInfoProps, child: string[]) => string;
|
|
31
36
|
APIExample: (child: string[]) => string;
|
|
@@ -41,7 +46,9 @@ interface Renderer {
|
|
|
41
46
|
*/
|
|
42
47
|
ObjectCollapsible: (props: ObjectCollapsibleProps, child: string[]) => string;
|
|
43
48
|
Property: (props: PropertyProps, child: string[]) => string;
|
|
49
|
+
APIPlayground: (props: APIPlaygroundProps) => string;
|
|
44
50
|
}
|
|
51
|
+
|
|
45
52
|
declare const defaultRenderer: Renderer;
|
|
46
53
|
|
|
47
54
|
interface CodeSample {
|
|
@@ -147,4 +154,4 @@ declare function generateFiles({ input, output, name: nameFn, per, cwd, ...optio
|
|
|
147
154
|
|
|
148
155
|
declare function createElement(name: string, props: object, ...child: string[]): string;
|
|
149
156
|
|
|
150
|
-
export { type APIInfoProps, type Config, type GenerateOperationOutput, type GenerateOptions, type GenerateTagOutput, type MethodInformation, type ObjectCollapsibleProps, type PropertyProps, type RenderContext, type Renderer, type RequestProps, type ResponseProps, type ResponsesProps, type RouteInformation, createElement, defaultRenderer, generate, generateFiles, generateOperations, generateTags };
|
|
157
|
+
export { type APIInfoProps, APIPlaygroundProps, type Config, type GenerateOperationOutput, type GenerateOptions, type GenerateTagOutput, type MethodInformation, type ObjectCollapsibleProps, type PropertyProps, type RenderContext, type Renderer, type RequestProps, type ResponseProps, type ResponsesProps, type RootProps, type RouteInformation, createElement, defaultRenderer, generate, generateFiles, generateOperations, generateTags };
|
package/dist/index.js
CHANGED
|
@@ -46,7 +46,7 @@ function buildOperation(method, operation) {
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
// src/
|
|
49
|
+
// src/utils/generate-document.ts
|
|
50
50
|
import { dump } from "js-yaml";
|
|
51
51
|
|
|
52
52
|
// src/render/element.ts
|
|
@@ -78,7 +78,7 @@ function heading(depth, child) {
|
|
|
78
78
|
|
|
79
79
|
// src/render/renderer.ts
|
|
80
80
|
var defaultRenderer = {
|
|
81
|
-
Root: (child) => createElement("Root",
|
|
81
|
+
Root: (props, child) => createElement("Root", props, ...child),
|
|
82
82
|
API: (child) => createElement("API", {}, ...child),
|
|
83
83
|
APIInfo: (props, child) => createElement("APIInfo", props, ...child),
|
|
84
84
|
APIExample: (child) => createElement("APIExample", {}, ...child),
|
|
@@ -94,11 +94,12 @@ var defaultRenderer = {
|
|
|
94
94
|
Property: (props, child) => createElement("Property", props, ...child),
|
|
95
95
|
ObjectCollapsible: (props, child) => createElement("ObjectCollapsible", props, ...child),
|
|
96
96
|
Requests: (items, child) => createElement("Requests", { items }, ...child),
|
|
97
|
-
Request: ({ language, code, name }) => createElement("Request", { value: name }, codeblock({ language }, code))
|
|
97
|
+
Request: ({ language, code, name }) => createElement("Request", { value: name }, codeblock({ language }, code)),
|
|
98
|
+
APIPlayground: (props) => createElement("APIPlayground", props)
|
|
98
99
|
};
|
|
99
100
|
|
|
100
|
-
// src/
|
|
101
|
-
function
|
|
101
|
+
// src/utils/generate-document.ts
|
|
102
|
+
function generateDocument(title, description, content, options) {
|
|
102
103
|
const banner = dump({
|
|
103
104
|
title,
|
|
104
105
|
description,
|
|
@@ -108,19 +109,30 @@ function renderPage(title, description, content, options) {
|
|
|
108
109
|
const finalImports = (options.imports ?? [
|
|
109
110
|
{
|
|
110
111
|
names: Object.keys(defaultRenderer),
|
|
111
|
-
from: "fumadocs-ui
|
|
112
|
+
from: "fumadocs-openapi/ui"
|
|
112
113
|
}
|
|
113
114
|
]).map(
|
|
114
115
|
(item) => `import { ${item.names.join(", ")} } from ${JSON.stringify(item.from)};`
|
|
115
116
|
).join("\n");
|
|
116
|
-
const Root = options.renderer?.Root ?? defaultRenderer.Root;
|
|
117
117
|
return `---
|
|
118
118
|
${banner}
|
|
119
119
|
---
|
|
120
120
|
|
|
121
121
|
${finalImports}
|
|
122
122
|
|
|
123
|
-
${
|
|
123
|
+
${content}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/utils/id-to-title.ts
|
|
127
|
+
function idToTitle(id) {
|
|
128
|
+
const result = [];
|
|
129
|
+
for (const c of id) {
|
|
130
|
+
if (result.length === 0) result.push(c.toLocaleUpperCase());
|
|
131
|
+
else if (/^[A-Z]$/.test(c) && result.at(-1) !== " ") result.push(" ", c);
|
|
132
|
+
else if (c === "-") result.push(" ");
|
|
133
|
+
else result.push(c);
|
|
134
|
+
}
|
|
135
|
+
return result.join("");
|
|
124
136
|
}
|
|
125
137
|
|
|
126
138
|
// src/utils/schema.ts
|
|
@@ -266,6 +278,160 @@ async function getTypescriptSchema(endpoint, code) {
|
|
|
266
278
|
}
|
|
267
279
|
}
|
|
268
280
|
|
|
281
|
+
// src/utils/get-security.ts
|
|
282
|
+
function getScheme(requirement, document) {
|
|
283
|
+
const results = [];
|
|
284
|
+
const schemas = document.components?.securitySchemes ?? {};
|
|
285
|
+
for (const [key, scopes] of Object.entries(requirement)) {
|
|
286
|
+
if (!(key in schemas)) return [];
|
|
287
|
+
const schema = noRef(schemas[key]);
|
|
288
|
+
results.push({
|
|
289
|
+
...schema,
|
|
290
|
+
scopes
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
return results;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// src/render/playground.ts
|
|
297
|
+
function renderPlayground(path, method, ctx) {
|
|
298
|
+
let currentId = 0;
|
|
299
|
+
const context = {
|
|
300
|
+
schema: {},
|
|
301
|
+
nextId() {
|
|
302
|
+
return String(currentId++);
|
|
303
|
+
},
|
|
304
|
+
registered: /* @__PURE__ */ new WeakMap()
|
|
305
|
+
};
|
|
306
|
+
const body = method.requestBody ? getPreferredMedia(noRef(method.requestBody).content) : void 0;
|
|
307
|
+
return ctx.renderer.APIPlayground({
|
|
308
|
+
authorization: getAuthorizationField(method, ctx),
|
|
309
|
+
method: method.method,
|
|
310
|
+
route: path,
|
|
311
|
+
path: method.parameters.filter((v) => v.in === "path").map((v) => parameterToField(v, context)),
|
|
312
|
+
query: method.parameters.filter((v) => v.in === "query").map((v) => parameterToField(v, context)),
|
|
313
|
+
header: method.parameters.filter((v) => v.in === "header").map((v) => parameterToField(v, context)),
|
|
314
|
+
body: body?.schema ? toSchema(noRef(body.schema), true, context) : void 0,
|
|
315
|
+
schemas: context.schema
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
function getAuthorizationField(method, ctx) {
|
|
319
|
+
const security = method.security ?? ctx.document.security ?? [];
|
|
320
|
+
if (security.length === 0) return;
|
|
321
|
+
const singular = security.find(
|
|
322
|
+
(requirements) => Object.keys(requirements).length === 1
|
|
323
|
+
);
|
|
324
|
+
if (!singular) return;
|
|
325
|
+
const scheme = getScheme(singular, ctx.document)[0];
|
|
326
|
+
return {
|
|
327
|
+
type: "string",
|
|
328
|
+
name: "Authorization",
|
|
329
|
+
defaultValue: scheme.type === "oauth2" || scheme.type === "http" && scheme.scheme === "bearer" ? "Bearer" : "Basic",
|
|
330
|
+
isRequired: security.every(
|
|
331
|
+
(requirements) => Object.keys(requirements).length > 0
|
|
332
|
+
),
|
|
333
|
+
description: "The Authorization access token"
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
function getIdFromSchema(schema, required, ctx) {
|
|
337
|
+
const registered = ctx.registered.get(schema);
|
|
338
|
+
if (registered === void 0) {
|
|
339
|
+
const id = ctx.nextId();
|
|
340
|
+
ctx.registered.set(schema, id);
|
|
341
|
+
ctx.schema[id] = toSchema(schema, required, ctx);
|
|
342
|
+
return id;
|
|
343
|
+
}
|
|
344
|
+
return registered;
|
|
345
|
+
}
|
|
346
|
+
function parameterToField(v, ctx) {
|
|
347
|
+
return {
|
|
348
|
+
name: v.name,
|
|
349
|
+
...toSchema(
|
|
350
|
+
noRef(v.schema) ?? { type: "string" },
|
|
351
|
+
v.required ?? false,
|
|
352
|
+
ctx
|
|
353
|
+
)
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
function toReference(schema, required, ctx) {
|
|
357
|
+
return {
|
|
358
|
+
type: "ref",
|
|
359
|
+
isRequired: required,
|
|
360
|
+
schema: getIdFromSchema(schema, false, ctx)
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
function toSchema(schema, required, ctx) {
|
|
364
|
+
if (schema.type === "array") {
|
|
365
|
+
return {
|
|
366
|
+
type: "array",
|
|
367
|
+
description: schema.description ?? schema.title,
|
|
368
|
+
isRequired: required,
|
|
369
|
+
items: getIdFromSchema(noRef(schema.items), false, ctx)
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
if (schema.type === "object" || schema.properties !== void 0 || schema.allOf !== void 0) {
|
|
373
|
+
const properties = {};
|
|
374
|
+
Object.entries(schema.properties ?? {}).forEach(([key, prop]) => {
|
|
375
|
+
properties[key] = toReference(
|
|
376
|
+
noRef(prop),
|
|
377
|
+
schema.required?.includes(key) ?? false,
|
|
378
|
+
ctx
|
|
379
|
+
);
|
|
380
|
+
});
|
|
381
|
+
schema.allOf?.forEach((c) => {
|
|
382
|
+
const field = toSchema(noRef(c), true, ctx);
|
|
383
|
+
if (field.type === "object") Object.assign(properties, field.properties);
|
|
384
|
+
});
|
|
385
|
+
const additional = noRef(schema.additionalProperties);
|
|
386
|
+
let additionalProperties;
|
|
387
|
+
if (additional && typeof additional === "object") {
|
|
388
|
+
if (!additional.type && !additional.anyOf && !additional.allOf && !additional.oneOf) {
|
|
389
|
+
additionalProperties = true;
|
|
390
|
+
} else {
|
|
391
|
+
additionalProperties = getIdFromSchema(additional, false, ctx);
|
|
392
|
+
}
|
|
393
|
+
} else {
|
|
394
|
+
additionalProperties = additional;
|
|
395
|
+
}
|
|
396
|
+
return {
|
|
397
|
+
type: "object",
|
|
398
|
+
isRequired: required,
|
|
399
|
+
description: schema.description ?? schema.title,
|
|
400
|
+
properties,
|
|
401
|
+
additionalProperties
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
if (schema.type === void 0) {
|
|
405
|
+
const combine = schema.anyOf ?? schema.oneOf;
|
|
406
|
+
if (combine) {
|
|
407
|
+
return {
|
|
408
|
+
type: "switcher",
|
|
409
|
+
description: schema.description ?? schema.title,
|
|
410
|
+
items: Object.fromEntries(
|
|
411
|
+
combine.map((c, idx) => {
|
|
412
|
+
const item = noRef(c);
|
|
413
|
+
return [
|
|
414
|
+
item.title ?? item.type ?? `Item ${idx.toString()}`,
|
|
415
|
+
toReference(item, true, ctx)
|
|
416
|
+
];
|
|
417
|
+
})
|
|
418
|
+
),
|
|
419
|
+
isRequired: required
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
return {
|
|
423
|
+
type: "null",
|
|
424
|
+
isRequired: false
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
return {
|
|
428
|
+
type: schema.type === "integer" ? "number" : schema.type,
|
|
429
|
+
defaultValue: schema.example ?? "",
|
|
430
|
+
isRequired: required,
|
|
431
|
+
description: schema.description ?? schema.title
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
|
|
269
435
|
// src/render/schema.ts
|
|
270
436
|
var keys = {
|
|
271
437
|
example: "Example",
|
|
@@ -278,7 +444,7 @@ var keys = {
|
|
|
278
444
|
format: "Format"
|
|
279
445
|
};
|
|
280
446
|
function isObject(schema) {
|
|
281
|
-
return schema.type === "object" || schema.properties !== void 0;
|
|
447
|
+
return schema.type === "object" || schema.properties !== void 0 || schema.additionalProperties !== void 0;
|
|
282
448
|
}
|
|
283
449
|
function schemaElement(name, schema, ctx) {
|
|
284
450
|
return render(name, schema, {
|
|
@@ -348,9 +514,19 @@ function render(name, schema, ctx) {
|
|
|
348
514
|
})
|
|
349
515
|
])
|
|
350
516
|
);
|
|
517
|
+
} else if (schema.allOf) {
|
|
518
|
+
child.push(
|
|
519
|
+
renderer.ObjectCollapsible({ name }, [
|
|
520
|
+
render(name, combineSchema(schema.allOf.map(noRef)), {
|
|
521
|
+
...ctx,
|
|
522
|
+
parseObject: true,
|
|
523
|
+
required: false
|
|
524
|
+
})
|
|
525
|
+
])
|
|
526
|
+
);
|
|
351
527
|
} else {
|
|
352
528
|
const mentionedObjectTypes = [
|
|
353
|
-
...schema.anyOf ?? schema.oneOf ??
|
|
529
|
+
...schema.anyOf ?? schema.oneOf ?? [],
|
|
354
530
|
...schema.not ? [schema.not] : [],
|
|
355
531
|
...schema.type === "array" ? [schema.items] : []
|
|
356
532
|
].map(noRef).filter((s) => isComplexType(s) && !ctx.stack.includes(s));
|
|
@@ -381,6 +557,28 @@ function render(name, schema, ctx) {
|
|
|
381
557
|
child
|
|
382
558
|
);
|
|
383
559
|
}
|
|
560
|
+
function combineSchema(schema) {
|
|
561
|
+
const result = {
|
|
562
|
+
type: "object"
|
|
563
|
+
};
|
|
564
|
+
function add(s) {
|
|
565
|
+
result.properties ??= {};
|
|
566
|
+
if (s.properties) {
|
|
567
|
+
Object.assign(result.properties, s.properties);
|
|
568
|
+
}
|
|
569
|
+
result.additionalProperties ??= {};
|
|
570
|
+
if (s.additionalProperties === true) {
|
|
571
|
+
result.additionalProperties = true;
|
|
572
|
+
} else if (s.additionalProperties && typeof result.additionalProperties !== "boolean") {
|
|
573
|
+
Object.assign(result.additionalProperties, s.additionalProperties);
|
|
574
|
+
}
|
|
575
|
+
if (s.allOf) {
|
|
576
|
+
add(combineSchema(s.allOf.map(noRef)));
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
schema.forEach(add);
|
|
580
|
+
return result;
|
|
581
|
+
}
|
|
384
582
|
function isComplexType(schema) {
|
|
385
583
|
if (schema.anyOf ?? schema.oneOf ?? schema.allOf) return true;
|
|
386
584
|
return isObject(schema) || schema.type === "array";
|
|
@@ -413,9 +611,13 @@ async function renderOperation(path, method, ctx, noTitle = false) {
|
|
|
413
611
|
const security = method.security ?? ctx.document.security;
|
|
414
612
|
const info = [];
|
|
415
613
|
const example = [];
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
614
|
+
if (!noTitle) {
|
|
615
|
+
info.push(
|
|
616
|
+
heading(
|
|
617
|
+
level,
|
|
618
|
+
method.summary ?? (method.operationId ? idToTitle(method.operationId) : path)
|
|
619
|
+
)
|
|
620
|
+
);
|
|
419
621
|
level++;
|
|
420
622
|
}
|
|
421
623
|
if (method.description) info.push(p(method.description));
|
|
@@ -474,6 +676,7 @@ async function renderOperation(path, method, ctx, noTitle = false) {
|
|
|
474
676
|
info.push(heading(level, group), ...parameters);
|
|
475
677
|
}
|
|
476
678
|
info.push(getResponseTable(method));
|
|
679
|
+
info.push(renderPlayground(path, method, ctx));
|
|
477
680
|
const samples = dedupe([
|
|
478
681
|
{
|
|
479
682
|
label: "cURL",
|
|
@@ -518,12 +721,9 @@ function dedupe(samples) {
|
|
|
518
721
|
}
|
|
519
722
|
function getAuthSection(requirements, { document, renderer }) {
|
|
520
723
|
const info = [];
|
|
521
|
-
const schemas = document.components?.securitySchemes ?? {};
|
|
522
724
|
for (const requirement of requirements) {
|
|
523
725
|
if (info.length > 0) info.push(`---`);
|
|
524
|
-
for (const
|
|
525
|
-
if (!(name in schemas)) continue;
|
|
526
|
-
const schema = noRef(schemas[name]);
|
|
726
|
+
for (const schema of getScheme(requirement, document)) {
|
|
527
727
|
if (schema.type === "http") {
|
|
528
728
|
info.push(
|
|
529
729
|
renderer.Property(
|
|
@@ -550,7 +750,7 @@ function getAuthSection(requirements, { document, renderer }) {
|
|
|
550
750
|
[
|
|
551
751
|
p(schema.description),
|
|
552
752
|
`In: \`header\``,
|
|
553
|
-
`Scope: \`${scopes.length > 0 ? scopes.join(", ") : "none"}\``
|
|
753
|
+
`Scope: \`${schema.scopes.length > 0 ? schema.scopes.join(", ") : "none"}\``
|
|
554
754
|
]
|
|
555
755
|
)
|
|
556
756
|
);
|
|
@@ -632,10 +832,10 @@ async function generate(pathOrDocument, options = {}) {
|
|
|
632
832
|
child.push(await renderOperation(route.path, method, ctx));
|
|
633
833
|
}
|
|
634
834
|
}
|
|
635
|
-
return
|
|
835
|
+
return generateDocument(
|
|
636
836
|
document.info.title,
|
|
637
837
|
document.info.description,
|
|
638
|
-
child,
|
|
838
|
+
ctx.renderer.Root({ baseUrl: ctx.baseUrl }, child),
|
|
639
839
|
options
|
|
640
840
|
);
|
|
641
841
|
}
|
|
@@ -646,10 +846,12 @@ async function generateOperations(pathOrDocument, options = {}) {
|
|
|
646
846
|
return await Promise.all(
|
|
647
847
|
routes.flatMap((route) => {
|
|
648
848
|
return route.methods.map(async (method) => {
|
|
649
|
-
const content =
|
|
849
|
+
const content = generateDocument(
|
|
650
850
|
method.summary ?? method.method,
|
|
651
851
|
method.description,
|
|
652
|
-
|
|
852
|
+
ctx.renderer.Root({ baseUrl: ctx.baseUrl }, [
|
|
853
|
+
await renderOperation(route.path, method, ctx, true)
|
|
854
|
+
]),
|
|
653
855
|
options
|
|
654
856
|
);
|
|
655
857
|
if (!method.operationId)
|
|
@@ -677,7 +879,12 @@ async function generateTags(pathOrDocument, options = {}) {
|
|
|
677
879
|
}
|
|
678
880
|
return {
|
|
679
881
|
tag,
|
|
680
|
-
content:
|
|
882
|
+
content: generateDocument(
|
|
883
|
+
idToTitle(tag),
|
|
884
|
+
info?.description,
|
|
885
|
+
ctx.renderer.Root({ baseUrl: ctx.baseUrl }, child),
|
|
886
|
+
options
|
|
887
|
+
)
|
|
681
888
|
};
|
|
682
889
|
})
|
|
683
890
|
);
|
|
@@ -694,8 +901,8 @@ function getContext(document, options) {
|
|
|
694
901
|
}
|
|
695
902
|
|
|
696
903
|
// src/generate-file.ts
|
|
697
|
-
import { mkdir, writeFile } from "
|
|
698
|
-
import { dirname, join, parse } from "
|
|
904
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
905
|
+
import { dirname, join, parse } from "path";
|
|
699
906
|
import fg from "fast-glob";
|
|
700
907
|
async function generateFiles({
|
|
701
908
|
input,
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
interface BaseRequestField {
|
|
2
|
+
name: string;
|
|
3
|
+
description?: string;
|
|
4
|
+
}
|
|
5
|
+
interface BaseSchema {
|
|
6
|
+
description?: string;
|
|
7
|
+
isRequired: boolean;
|
|
8
|
+
}
|
|
9
|
+
type PrimitiveRequestField = BaseRequestField & PrimitiveSchema;
|
|
10
|
+
interface PrimitiveSchema extends BaseSchema {
|
|
11
|
+
type: 'boolean' | 'string' | 'number';
|
|
12
|
+
defaultValue: string;
|
|
13
|
+
}
|
|
14
|
+
interface ReferenceSchema extends BaseSchema {
|
|
15
|
+
type: 'ref';
|
|
16
|
+
schema: string;
|
|
17
|
+
}
|
|
18
|
+
interface ArraySchema extends BaseSchema {
|
|
19
|
+
type: 'array';
|
|
20
|
+
/**
|
|
21
|
+
* Reference to item schema or the schema
|
|
22
|
+
*/
|
|
23
|
+
items: string | RequestSchema;
|
|
24
|
+
}
|
|
25
|
+
interface ObjectSchema extends BaseSchema {
|
|
26
|
+
type: 'object';
|
|
27
|
+
properties: Record<string, ReferenceSchema>;
|
|
28
|
+
/**
|
|
29
|
+
* Reference to schema, or true if it's `any`
|
|
30
|
+
*/
|
|
31
|
+
additionalProperties?: boolean | string;
|
|
32
|
+
}
|
|
33
|
+
interface SwitcherSchema extends BaseSchema {
|
|
34
|
+
type: 'switcher';
|
|
35
|
+
items: Record<string, ReferenceSchema | RequestSchema>;
|
|
36
|
+
}
|
|
37
|
+
interface NullSchema extends BaseSchema {
|
|
38
|
+
type: 'null';
|
|
39
|
+
}
|
|
40
|
+
type RequestSchema = PrimitiveSchema | ArraySchema | ObjectSchema | SwitcherSchema | NullSchema;
|
|
41
|
+
interface APIPlaygroundProps {
|
|
42
|
+
route: string;
|
|
43
|
+
method: string;
|
|
44
|
+
authorization?: PrimitiveRequestField;
|
|
45
|
+
path?: PrimitiveRequestField[];
|
|
46
|
+
query?: PrimitiveRequestField[];
|
|
47
|
+
header?: PrimitiveRequestField[];
|
|
48
|
+
body?: RequestSchema;
|
|
49
|
+
schemas: Record<string, RequestSchema>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type { APIPlaygroundProps as A, PrimitiveRequestField as P, RequestSchema as R, ReferenceSchema as a };
|