fumadocs-openapi 3.1.3 → 3.3.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/index.d.ts +46 -16
- package/dist/index.js +92 -47
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -44,6 +44,48 @@ interface Renderer {
|
|
|
44
44
|
}
|
|
45
45
|
declare const defaultRenderer: Renderer;
|
|
46
46
|
|
|
47
|
+
interface CodeSample {
|
|
48
|
+
lang: string;
|
|
49
|
+
label: string;
|
|
50
|
+
source: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface RouteInformation {
|
|
54
|
+
path: string;
|
|
55
|
+
summary?: string;
|
|
56
|
+
description?: string;
|
|
57
|
+
methods: MethodInformation[];
|
|
58
|
+
}
|
|
59
|
+
interface MethodInformation extends OpenAPIV3.OperationObject {
|
|
60
|
+
parameters: OpenAPIV3.ParameterObject[];
|
|
61
|
+
method: string;
|
|
62
|
+
}
|
|
63
|
+
interface RenderContext {
|
|
64
|
+
renderer: Renderer;
|
|
65
|
+
document: OpenAPIV3.Document;
|
|
66
|
+
baseUrl: string;
|
|
67
|
+
generateCodeSamples?: (endpoint: Endpoint) => CodeSample[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface Endpoint {
|
|
71
|
+
/**
|
|
72
|
+
* URL, including path and query parameters
|
|
73
|
+
*/
|
|
74
|
+
url: string;
|
|
75
|
+
method: string;
|
|
76
|
+
body?: unknown;
|
|
77
|
+
responses: Record<string, Response>;
|
|
78
|
+
parameters: Parameter[];
|
|
79
|
+
}
|
|
80
|
+
interface Response {
|
|
81
|
+
schema: OpenAPIV3.SchemaObject;
|
|
82
|
+
}
|
|
83
|
+
interface Parameter {
|
|
84
|
+
name: string;
|
|
85
|
+
in: string;
|
|
86
|
+
schema: OpenAPIV3.SchemaObject;
|
|
87
|
+
}
|
|
88
|
+
|
|
47
89
|
interface GenerateOptions {
|
|
48
90
|
/**
|
|
49
91
|
* The imports of your MDX components.
|
|
@@ -60,6 +102,10 @@ interface GenerateOptions {
|
|
|
60
102
|
* A `full: true` property will be added by default.
|
|
61
103
|
*/
|
|
62
104
|
frontmatter?: (title: string, description: string | undefined) => Record<string, unknown>;
|
|
105
|
+
/**
|
|
106
|
+
* Generate code samples for endpoint
|
|
107
|
+
*/
|
|
108
|
+
generateCodeSamples?: (endpoint: Endpoint) => CodeSample[];
|
|
63
109
|
renderer?: Partial<Renderer>;
|
|
64
110
|
}
|
|
65
111
|
interface GenerateTagOutput {
|
|
@@ -99,22 +145,6 @@ interface Config extends GenerateOptions {
|
|
|
99
145
|
}
|
|
100
146
|
declare function generateFiles({ input, output, name: nameFn, per, cwd, ...options }: Config): Promise<void>;
|
|
101
147
|
|
|
102
|
-
interface RouteInformation {
|
|
103
|
-
path: string;
|
|
104
|
-
summary?: string;
|
|
105
|
-
description?: string;
|
|
106
|
-
methods: MethodInformation[];
|
|
107
|
-
}
|
|
108
|
-
interface MethodInformation extends OpenAPIV3.OperationObject {
|
|
109
|
-
parameters: OpenAPIV3.ParameterObject[];
|
|
110
|
-
method: string;
|
|
111
|
-
}
|
|
112
|
-
interface RenderContext {
|
|
113
|
-
renderer: Renderer;
|
|
114
|
-
document: OpenAPIV3.Document;
|
|
115
|
-
baseUrl: string;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
148
|
declare function createElement(name: string, props: object, ...child: string[]): string;
|
|
119
149
|
|
|
120
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 };
|
package/dist/index.js
CHANGED
|
@@ -281,39 +281,43 @@ function isObject(schema) {
|
|
|
281
281
|
return schema.type === "object" || schema.properties !== void 0;
|
|
282
282
|
}
|
|
283
283
|
function schemaElement(name, schema, ctx) {
|
|
284
|
+
return render(name, schema, {
|
|
285
|
+
...ctx,
|
|
286
|
+
stack: []
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
function render(name, schema, ctx) {
|
|
284
290
|
if (schema.readOnly && !ctx.readOnly) return "";
|
|
285
291
|
if (schema.writeOnly && !ctx.writeOnly) return "";
|
|
286
292
|
const { renderer } = ctx.render;
|
|
287
293
|
const child = [];
|
|
288
294
|
function field(key, value) {
|
|
289
|
-
child.push(span(`${key}:
|
|
295
|
+
child.push(span(`${key}: \`${value}\``));
|
|
290
296
|
}
|
|
291
297
|
if (isObject(schema) && ctx.parseObject) {
|
|
292
298
|
const { additionalProperties, properties } = schema;
|
|
293
|
-
if (additionalProperties) {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
);
|
|
312
|
-
}
|
|
299
|
+
if (additionalProperties === true) {
|
|
300
|
+
child.push(
|
|
301
|
+
renderer.Property(
|
|
302
|
+
{
|
|
303
|
+
name: "[key: string]",
|
|
304
|
+
type: "any"
|
|
305
|
+
},
|
|
306
|
+
[]
|
|
307
|
+
)
|
|
308
|
+
);
|
|
309
|
+
} else if (additionalProperties) {
|
|
310
|
+
child.push(
|
|
311
|
+
render("[key: string]", noRef(additionalProperties), {
|
|
312
|
+
...ctx,
|
|
313
|
+
required: false,
|
|
314
|
+
parseObject: false
|
|
315
|
+
})
|
|
316
|
+
);
|
|
313
317
|
}
|
|
314
318
|
Object.entries(properties ?? {}).forEach(([key, value]) => {
|
|
315
319
|
child.push(
|
|
316
|
-
|
|
320
|
+
render(key, noRef(value), {
|
|
317
321
|
...ctx,
|
|
318
322
|
required: schema.required?.includes(key) ?? false,
|
|
319
323
|
parseObject: false
|
|
@@ -334,17 +338,38 @@ function schemaElement(name, schema, ctx) {
|
|
|
334
338
|
schema.enum.map((value) => JSON.stringify(value)).join(" | ")
|
|
335
339
|
);
|
|
336
340
|
}
|
|
337
|
-
|
|
338
|
-
if (resolved && !ctx.parseObject) {
|
|
341
|
+
if (isObject(schema) && !ctx.parseObject) {
|
|
339
342
|
child.push(
|
|
340
343
|
renderer.ObjectCollapsible({ name }, [
|
|
341
|
-
|
|
344
|
+
render(name, schema, {
|
|
342
345
|
...ctx,
|
|
343
346
|
parseObject: true,
|
|
344
347
|
required: false
|
|
345
348
|
})
|
|
346
349
|
])
|
|
347
350
|
);
|
|
351
|
+
} else {
|
|
352
|
+
const mentionedObjectTypes = [
|
|
353
|
+
...schema.anyOf ?? schema.oneOf ?? schema.allOf ?? [],
|
|
354
|
+
...schema.not ? [schema.not] : [],
|
|
355
|
+
...schema.type === "array" ? [schema.items] : []
|
|
356
|
+
].map(noRef).filter((s) => isComplexType(s) && !ctx.stack.includes(s));
|
|
357
|
+
ctx.stack.push(schema);
|
|
358
|
+
child.push(
|
|
359
|
+
...mentionedObjectTypes.map(
|
|
360
|
+
(s, idx) => renderer.ObjectCollapsible(
|
|
361
|
+
{ name: s.title ?? `Object ${(idx + 1).toString()}` },
|
|
362
|
+
[
|
|
363
|
+
render("element", noRef(s), {
|
|
364
|
+
...ctx,
|
|
365
|
+
parseObject: true,
|
|
366
|
+
required: false
|
|
367
|
+
})
|
|
368
|
+
]
|
|
369
|
+
)
|
|
370
|
+
)
|
|
371
|
+
);
|
|
372
|
+
ctx.stack.pop();
|
|
348
373
|
}
|
|
349
374
|
return renderer.Property(
|
|
350
375
|
{
|
|
@@ -356,28 +381,29 @@ function schemaElement(name, schema, ctx) {
|
|
|
356
381
|
child
|
|
357
382
|
);
|
|
358
383
|
}
|
|
359
|
-
function
|
|
360
|
-
if (
|
|
361
|
-
|
|
362
|
-
return resolveObjectType(noRef(schema.items));
|
|
363
|
-
}
|
|
384
|
+
function isComplexType(schema) {
|
|
385
|
+
if (schema.anyOf ?? schema.oneOf ?? schema.allOf) return true;
|
|
386
|
+
return isObject(schema) || schema.type === "array";
|
|
364
387
|
}
|
|
365
388
|
function getSchemaType(schema) {
|
|
366
389
|
if (schema.nullable) {
|
|
367
|
-
|
|
368
|
-
return
|
|
390
|
+
const type = getSchemaType({ ...schema, nullable: false });
|
|
391
|
+
return type === "unknown" ? "null" : `${type} | null`;
|
|
369
392
|
}
|
|
393
|
+
if (schema.title) return schema.title;
|
|
370
394
|
if (schema.type === "array")
|
|
371
|
-
return `array
|
|
395
|
+
return `array<${getSchemaType(noRef(schema.items))}>`;
|
|
372
396
|
if (schema.oneOf)
|
|
373
397
|
return schema.oneOf.map((one) => getSchemaType(noRef(one))).join(" | ");
|
|
374
398
|
if (schema.allOf)
|
|
375
399
|
return schema.allOf.map((one) => getSchemaType(noRef(one))).join(" & ");
|
|
376
|
-
if (schema.
|
|
400
|
+
if (schema.not) return `not ${getSchemaType(noRef(schema.not))}`;
|
|
401
|
+
if (schema.anyOf) {
|
|
377
402
|
return `Any properties in ${schema.anyOf.map((one) => getSchemaType(noRef(one))).join(", ")}`;
|
|
403
|
+
}
|
|
378
404
|
if (schema.type) return schema.type;
|
|
379
405
|
if (isObject(schema)) return "object";
|
|
380
|
-
|
|
406
|
+
return "unknown";
|
|
381
407
|
}
|
|
382
408
|
|
|
383
409
|
// src/render/operation.ts
|
|
@@ -448,21 +474,30 @@ async function renderOperation(path, method, ctx, noTitle = false) {
|
|
|
448
474
|
info.push(heading(level, group), ...parameters);
|
|
449
475
|
}
|
|
450
476
|
info.push(getResponseTable(method));
|
|
477
|
+
const samples = dedupe([
|
|
478
|
+
{
|
|
479
|
+
label: "cURL",
|
|
480
|
+
source: getSampleRequest(endpoint),
|
|
481
|
+
lang: "bash"
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
label: "JavaScript",
|
|
485
|
+
source: getSampleRequest2(endpoint),
|
|
486
|
+
lang: "js"
|
|
487
|
+
},
|
|
488
|
+
...ctx.generateCodeSamples?.(endpoint) ?? [],
|
|
489
|
+
...method["x-codeSamples"] ?? []
|
|
490
|
+
]);
|
|
451
491
|
example.push(
|
|
452
492
|
ctx.renderer.Requests(
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
ctx.renderer.Request({
|
|
456
|
-
name:
|
|
457
|
-
code:
|
|
458
|
-
language:
|
|
459
|
-
}),
|
|
460
|
-
ctx.renderer.Request({
|
|
461
|
-
name: "JavaScript",
|
|
462
|
-
code: getSampleRequest2(endpoint),
|
|
463
|
-
language: "js"
|
|
493
|
+
samples.map((s) => s.label),
|
|
494
|
+
samples.map(
|
|
495
|
+
(s) => ctx.renderer.Request({
|
|
496
|
+
name: s.label,
|
|
497
|
+
code: s.source,
|
|
498
|
+
language: s.lang
|
|
464
499
|
})
|
|
465
|
-
|
|
500
|
+
)
|
|
466
501
|
)
|
|
467
502
|
);
|
|
468
503
|
example.push(await getResponseTabs(endpoint, method, ctx));
|
|
@@ -471,6 +506,16 @@ async function renderOperation(path, method, ctx, noTitle = false) {
|
|
|
471
506
|
ctx.renderer.APIExample(example)
|
|
472
507
|
]);
|
|
473
508
|
}
|
|
509
|
+
function dedupe(samples) {
|
|
510
|
+
const set = /* @__PURE__ */ new Set();
|
|
511
|
+
const out = [];
|
|
512
|
+
for (let i = samples.length - 1; i >= 0; i--) {
|
|
513
|
+
if (set.has(samples[i].label)) continue;
|
|
514
|
+
set.add(samples[i].label);
|
|
515
|
+
out.unshift(samples[i]);
|
|
516
|
+
}
|
|
517
|
+
return out;
|
|
518
|
+
}
|
|
474
519
|
function getAuthSection(requirements, { document, renderer }) {
|
|
475
520
|
const info = [];
|
|
476
521
|
const schemas = document.components?.securitySchemes ?? {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fumadocs-openapi",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "Generate MDX docs for your OpenAPI spec",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"NextJs",
|
|
@@ -20,12 +20,12 @@
|
|
|
20
20
|
"@apidevtools/json-schema-ref-parser": "^11.6.4",
|
|
21
21
|
"fast-glob": "^3.3.1",
|
|
22
22
|
"js-yaml": "^4.1.0",
|
|
23
|
-
"json-schema-to-typescript": "^14.0
|
|
23
|
+
"json-schema-to-typescript": "^14.1.0",
|
|
24
24
|
"openapi-sampler": "^1.5.1"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/js-yaml": "^4.0.9",
|
|
28
|
-
"@types/node": "
|
|
28
|
+
"@types/node": "20.14.9",
|
|
29
29
|
"@types/openapi-sampler": "^1.0.3",
|
|
30
30
|
"openapi-types": "^12.1.3",
|
|
31
31
|
"eslint-config-custom": "0.0.0",
|