fumadocs-openapi 5.7.5 → 5.8.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 +59 -27
- package/dist/index.js +182 -141
- package/dist/server/index.d.ts +39 -12
- package/dist/server/index.js +321 -172
- package/dist/ui/{client-client-Ddkf37Pm.js → client-client-D9KEBAUd.js} +7 -7
- package/dist/ui/index.d.ts +27 -10
- package/dist/ui/index.js +6 -5
- package/dist/ui/{playground-client-DPxpLHKJ.js → playground-client-C0GkAwm2.js} +2 -2
- package/package.json +10 -9
package/dist/server/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { jsx,
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import Slugger from 'github-slugger';
|
|
3
|
-
import Parser from '@apidevtools/json-schema-ref-parser';
|
|
4
3
|
import { Fragment as Fragment$1 } from 'react';
|
|
5
4
|
import { sample } from 'openapi-sampler';
|
|
6
5
|
import { compile } from '@fumari/json-schema-to-typescript';
|
|
@@ -10,16 +9,15 @@ import { remark } from 'remark';
|
|
|
10
9
|
import remarkRehype from 'remark-rehype';
|
|
11
10
|
import { toJsxRuntime } from 'hast-util-to-jsx-runtime';
|
|
12
11
|
import { Heading } from 'fumadocs-ui/components/heading';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
12
|
+
import { Tabs, Tab } from 'fumadocs-ui/components/tabs';
|
|
13
|
+
import { Accordions, Accordion } from 'fumadocs-ui/components/accordion';
|
|
15
14
|
import * as Base from 'fumadocs-ui/components/codeblock';
|
|
16
15
|
import { highlight } from 'fumadocs-core/server';
|
|
17
|
-
import {
|
|
16
|
+
import { Root, API, APIInfo, APIExample as APIExample$1, Property, ObjectCollapsible, APIPlayground } from '../ui/index.js';
|
|
17
|
+
import Parser from '@apidevtools/json-schema-ref-parser';
|
|
18
|
+
import { upgrade } from '@scalar/openapi-parser';
|
|
18
19
|
import { cva } from 'class-variance-authority';
|
|
19
20
|
|
|
20
|
-
function noRef(v) {
|
|
21
|
-
return v;
|
|
22
|
-
}
|
|
23
21
|
function getPreferredType(body) {
|
|
24
22
|
if ('application/json' in body) return 'application/json';
|
|
25
23
|
return Object.keys(body)[0];
|
|
@@ -29,13 +27,21 @@ function getPreferredType(body) {
|
|
|
29
27
|
*/ function toSampleInput(value) {
|
|
30
28
|
return typeof value === 'string' ? value : JSON.stringify(value, null, 2);
|
|
31
29
|
}
|
|
30
|
+
function isNullable(schema, includeOneOf = true) {
|
|
31
|
+
if (Array.isArray(schema.type) && schema.type.includes('null')) return true;
|
|
32
|
+
if (includeOneOf && (schema.anyOf || schema.oneOf)) {
|
|
33
|
+
if (schema.anyOf?.some((item)=>isNullable(item))) return true;
|
|
34
|
+
if (schema.oneOf?.some((item)=>isNullable(item))) return true;
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
32
38
|
|
|
33
39
|
function getSecurities(requirement, document) {
|
|
34
40
|
const results = [];
|
|
35
41
|
const schemas = document.components?.securitySchemes ?? {};
|
|
36
42
|
for (const [key, scopes] of Object.entries(requirement)){
|
|
37
43
|
if (!(key in schemas)) return [];
|
|
38
|
-
const schema =
|
|
44
|
+
const schema = schemas[key];
|
|
39
45
|
results.push({
|
|
40
46
|
...schema,
|
|
41
47
|
scopes
|
|
@@ -54,22 +60,22 @@ function getSecurityPrefix(security) {
|
|
|
54
60
|
function generateSample(path, method, { baseUrl, document }) {
|
|
55
61
|
const params = [];
|
|
56
62
|
const responses = {};
|
|
57
|
-
for (const param of method.parameters){
|
|
63
|
+
for (const param of method.parameters ?? []){
|
|
58
64
|
if (param.schema) {
|
|
59
65
|
params.push({
|
|
60
66
|
name: param.name,
|
|
61
67
|
in: param.in,
|
|
62
|
-
schema:
|
|
68
|
+
schema: param.schema,
|
|
63
69
|
sample: param.example ?? sample(param.schema)
|
|
64
70
|
});
|
|
65
71
|
} else if (param.content) {
|
|
66
72
|
const key = getPreferredType(param.content);
|
|
67
73
|
const content = key ? param.content[key] : undefined;
|
|
68
|
-
if (!key || !content
|
|
74
|
+
if (!key || !content) throw new Error(`Cannot find parameter schema for ${param.name} in ${path} ${method.method}`);
|
|
69
75
|
params.push({
|
|
70
76
|
name: param.name,
|
|
71
77
|
in: param.in,
|
|
72
|
-
schema:
|
|
78
|
+
schema: content.schema ?? {},
|
|
73
79
|
sample: content.example ?? param.example ?? sample(content.schema)
|
|
74
80
|
});
|
|
75
81
|
}
|
|
@@ -90,22 +96,22 @@ function generateSample(path, method, { baseUrl, document }) {
|
|
|
90
96
|
}
|
|
91
97
|
let bodyOutput;
|
|
92
98
|
if (method.requestBody) {
|
|
93
|
-
const body =
|
|
99
|
+
const body = method.requestBody.content;
|
|
94
100
|
const type = getPreferredType(body);
|
|
95
|
-
|
|
96
|
-
|
|
101
|
+
if (!type) throw new Error(`Cannot find body schema for ${path} ${method.method}: missing media type`);
|
|
102
|
+
const schema = (type ? body[type].schema : undefined) ?? {};
|
|
97
103
|
bodyOutput = {
|
|
98
104
|
schema,
|
|
99
105
|
mediaType: type,
|
|
100
106
|
sample: body[type].example ?? generateBody(method.method, schema)
|
|
101
107
|
};
|
|
102
108
|
}
|
|
103
|
-
for (const [code,
|
|
104
|
-
const content =
|
|
109
|
+
for (const [code, response] of Object.entries(method.responses ?? {})){
|
|
110
|
+
const content = response.content;
|
|
105
111
|
if (!content) continue;
|
|
106
112
|
const mediaType = getPreferredType(content);
|
|
107
113
|
if (!mediaType) continue;
|
|
108
|
-
const responseSchema =
|
|
114
|
+
const responseSchema = content[mediaType].schema;
|
|
109
115
|
if (!responseSchema) continue;
|
|
110
116
|
responses[code] = {
|
|
111
117
|
mediaType,
|
|
@@ -272,13 +278,15 @@ response = requests.request("${endpoint.method}", url${variables.size > 0 ? `, $
|
|
|
272
278
|
print(response.text)`;
|
|
273
279
|
}
|
|
274
280
|
|
|
275
|
-
async function getTypescriptSchema(endpoint, code) {
|
|
281
|
+
async function getTypescriptSchema(endpoint, code, dereferenceMap) {
|
|
276
282
|
if (code in endpoint.responses) {
|
|
277
283
|
return compile(// re-running on the same schema results in error
|
|
278
284
|
// because it uses `defineProperty` to define internal references
|
|
279
285
|
// we clone the schema to fix this problem
|
|
280
|
-
|
|
286
|
+
// @ts-expect-error any types
|
|
287
|
+
endpoint.responses[code].schema, 'Response', {
|
|
281
288
|
$refOptions: false,
|
|
289
|
+
schemaToId: dereferenceMap,
|
|
282
290
|
bannerComment: '',
|
|
283
291
|
additionalProperties: false,
|
|
284
292
|
format: true,
|
|
@@ -289,7 +297,7 @@ async function getTypescriptSchema(endpoint, code) {
|
|
|
289
297
|
|
|
290
298
|
function Playground({ path, method, ctx }) {
|
|
291
299
|
let currentId = 0;
|
|
292
|
-
const bodyContent =
|
|
300
|
+
const bodyContent = method.requestBody?.content;
|
|
293
301
|
const mediaType = bodyContent ? getPreferredType(bodyContent) : undefined;
|
|
294
302
|
const context = {
|
|
295
303
|
allowFile: mediaType === 'multipart/form-data',
|
|
@@ -297,17 +305,18 @@ function Playground({ path, method, ctx }) {
|
|
|
297
305
|
nextId () {
|
|
298
306
|
return String(currentId++);
|
|
299
307
|
},
|
|
300
|
-
registered: new WeakMap()
|
|
308
|
+
registered: new WeakMap(),
|
|
309
|
+
render: ctx
|
|
301
310
|
};
|
|
302
311
|
const props = {
|
|
303
312
|
authorization: getAuthorizationField(method, ctx),
|
|
304
313
|
method: method.method,
|
|
305
314
|
route: path,
|
|
306
315
|
bodyType: mediaType === 'multipart/form-data' ? 'form-data' : 'json',
|
|
307
|
-
path: method.parameters
|
|
308
|
-
query: method.parameters
|
|
309
|
-
header: method.parameters
|
|
310
|
-
body: bodyContent && mediaType && bodyContent[mediaType].schema ? toSchema(
|
|
316
|
+
path: method.parameters?.filter((v)=>v.in === 'path').map((v)=>parameterToField(v, context)),
|
|
317
|
+
query: method.parameters?.filter((v)=>v.in === 'query').map((v)=>parameterToField(v, context)),
|
|
318
|
+
header: method.parameters?.filter((v)=>v.in === 'header').map((v)=>parameterToField(v, context)),
|
|
319
|
+
body: bodyContent && mediaType && bodyContent[mediaType].schema ? toSchema(bodyContent[mediaType].schema, true, context) : undefined,
|
|
311
320
|
schemas: context.schema
|
|
312
321
|
};
|
|
313
322
|
return /*#__PURE__*/ jsx(ctx.renderer.APIPlayground, {
|
|
@@ -342,7 +351,7 @@ function getIdFromSchema(schema, required, ctx) {
|
|
|
342
351
|
function parameterToField(v, ctx) {
|
|
343
352
|
return {
|
|
344
353
|
name: v.name,
|
|
345
|
-
...toSchema(
|
|
354
|
+
...toSchema(v.schema ?? {
|
|
346
355
|
type: 'string'
|
|
347
356
|
}, v.required ?? false, ctx)
|
|
348
357
|
};
|
|
@@ -360,19 +369,19 @@ function toSchema(schema, required, ctx) {
|
|
|
360
369
|
type: 'array',
|
|
361
370
|
description: schema.description ?? schema.title,
|
|
362
371
|
isRequired: required,
|
|
363
|
-
items: getIdFromSchema(
|
|
372
|
+
items: getIdFromSchema(schema.items, false, ctx)
|
|
364
373
|
};
|
|
365
374
|
}
|
|
366
375
|
if (schema.type === 'object' || schema.properties !== undefined || schema.allOf !== undefined) {
|
|
367
376
|
const properties = {};
|
|
368
377
|
Object.entries(schema.properties ?? {}).forEach(([key, prop])=>{
|
|
369
|
-
properties[key] = toReference(
|
|
378
|
+
properties[key] = toReference(prop, schema.required?.includes(key) ?? false, ctx);
|
|
370
379
|
});
|
|
371
380
|
schema.allOf?.forEach((c)=>{
|
|
372
|
-
const field = toSchema(
|
|
381
|
+
const field = toSchema(c, true, ctx);
|
|
373
382
|
if (field.type === 'object') Object.assign(properties, field.properties);
|
|
374
383
|
});
|
|
375
|
-
const additional =
|
|
384
|
+
const additional = schema.additionalProperties;
|
|
376
385
|
let additionalProperties;
|
|
377
386
|
if (additional && typeof additional === 'object') {
|
|
378
387
|
if (!additional.type && !additional.anyOf && !additional.allOf && !additional.oneOf) {
|
|
@@ -397,8 +406,7 @@ function toSchema(schema, required, ctx) {
|
|
|
397
406
|
return {
|
|
398
407
|
type: 'switcher',
|
|
399
408
|
description: schema.description ?? schema.title,
|
|
400
|
-
items: Object.fromEntries(combine.map((
|
|
401
|
-
const item = noRef(c);
|
|
409
|
+
items: Object.fromEntries(combine.map((item, idx)=>{
|
|
402
410
|
return [
|
|
403
411
|
item.title ?? item.type ?? `Item ${idx.toString()}`,
|
|
404
412
|
toReference(item, true, ctx)
|
|
@@ -419,6 +427,30 @@ function toSchema(schema, required, ctx) {
|
|
|
419
427
|
description: schema.description ?? schema.title
|
|
420
428
|
};
|
|
421
429
|
}
|
|
430
|
+
if (Array.isArray(schema.type)) {
|
|
431
|
+
const items = {};
|
|
432
|
+
for (const type of schema.type){
|
|
433
|
+
if (type === 'array') {
|
|
434
|
+
items[type] = {
|
|
435
|
+
type,
|
|
436
|
+
items: 'items' in schema && schema.items ? toSchema(schema.items, false, ctx) : toSchema({}, required, ctx),
|
|
437
|
+
isRequired: required
|
|
438
|
+
};
|
|
439
|
+
} else if (type !== 'null' && type !== 'object') {
|
|
440
|
+
items[type] = {
|
|
441
|
+
type: type === 'integer' ? 'number' : type,
|
|
442
|
+
isRequired: required,
|
|
443
|
+
defaultValue: schema.example ?? schema.default ?? ''
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return {
|
|
448
|
+
type: 'switcher',
|
|
449
|
+
description: schema.description ?? schema.title,
|
|
450
|
+
items,
|
|
451
|
+
isRequired: required
|
|
452
|
+
};
|
|
453
|
+
}
|
|
422
454
|
return {
|
|
423
455
|
type: schema.type === 'integer' ? 'number' : schema.type,
|
|
424
456
|
defaultValue: schema.example ?? '',
|
|
@@ -477,35 +509,45 @@ function heading(depth, child, ctx) {
|
|
|
477
509
|
};
|
|
478
510
|
function add(s) {
|
|
479
511
|
if (s.type) {
|
|
480
|
-
|
|
512
|
+
var _result;
|
|
513
|
+
(_result = result).type ?? (_result.type = []);
|
|
514
|
+
if (!Array.isArray(result.type)) {
|
|
515
|
+
result.type = [
|
|
516
|
+
result.type
|
|
517
|
+
];
|
|
518
|
+
}
|
|
519
|
+
for (const v of Array.isArray(s.type) ? s.type : [
|
|
520
|
+
s.type
|
|
521
|
+
]){
|
|
522
|
+
if (!result.type.includes(v)) {
|
|
523
|
+
result.type.push(v);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
481
526
|
}
|
|
482
527
|
if (s.properties) {
|
|
483
|
-
var
|
|
484
|
-
(
|
|
528
|
+
var _result1;
|
|
529
|
+
(_result1 = result).properties ?? (_result1.properties = {});
|
|
485
530
|
Object.assign(result.properties, s.properties);
|
|
486
531
|
}
|
|
487
532
|
if (s.additionalProperties === true) {
|
|
488
533
|
result.additionalProperties = true;
|
|
489
534
|
} else if (s.additionalProperties && typeof result.additionalProperties !== 'boolean') {
|
|
490
|
-
var
|
|
491
|
-
(
|
|
535
|
+
var _result2;
|
|
536
|
+
(_result2 = result).additionalProperties ?? (_result2.additionalProperties = {});
|
|
492
537
|
Object.assign(result.additionalProperties, s.additionalProperties);
|
|
493
538
|
}
|
|
494
539
|
if (s.required) {
|
|
495
|
-
var
|
|
496
|
-
(
|
|
540
|
+
var _result3;
|
|
541
|
+
(_result3 = result).required ?? (_result3.required = []);
|
|
497
542
|
result.required.push(...s.required);
|
|
498
543
|
}
|
|
499
544
|
if (s.enum && s.enum.length > 0) {
|
|
500
|
-
var
|
|
501
|
-
(
|
|
545
|
+
var _result4;
|
|
546
|
+
(_result4 = result).enum ?? (_result4.enum = []);
|
|
502
547
|
result.enum.push(...s.enum);
|
|
503
548
|
}
|
|
504
|
-
if (s.nullable) {
|
|
505
|
-
result.nullable = true;
|
|
506
|
-
}
|
|
507
549
|
if (s.allOf) {
|
|
508
|
-
|
|
550
|
+
s.allOf.forEach(add);
|
|
509
551
|
}
|
|
510
552
|
}
|
|
511
553
|
schema.forEach(add);
|
|
@@ -541,7 +583,7 @@ function Schema({ name, schema, ctx }) {
|
|
|
541
583
|
} else if (additionalProperties) {
|
|
542
584
|
child.push(/*#__PURE__*/ jsx(Schema, {
|
|
543
585
|
name: "[key: string]",
|
|
544
|
-
schema:
|
|
586
|
+
schema: additionalProperties,
|
|
545
587
|
ctx: {
|
|
546
588
|
...ctx,
|
|
547
589
|
required: false,
|
|
@@ -553,7 +595,7 @@ function Schema({ name, schema, ctx }) {
|
|
|
553
595
|
const rendered = Object.entries(properties).map(([key, value])=>{
|
|
554
596
|
return /*#__PURE__*/ jsx(Schema, {
|
|
555
597
|
name: key,
|
|
556
|
-
schema:
|
|
598
|
+
schema: value,
|
|
557
599
|
ctx: {
|
|
558
600
|
...ctx,
|
|
559
601
|
required: schema.required?.includes(key) ?? false,
|
|
@@ -568,7 +610,7 @@ function Schema({ name, schema, ctx }) {
|
|
|
568
610
|
if (schema.allOf && parseObject) {
|
|
569
611
|
return /*#__PURE__*/ jsx(Schema, {
|
|
570
612
|
name: name,
|
|
571
|
-
schema: combineSchema(
|
|
613
|
+
schema: combineSchema(schema.allOf),
|
|
572
614
|
ctx: ctx
|
|
573
615
|
});
|
|
574
616
|
}
|
|
@@ -602,7 +644,7 @@ function Schema({ name, schema, ctx }) {
|
|
|
602
644
|
]
|
|
603
645
|
}, field.key))
|
|
604
646
|
}, "fields"));
|
|
605
|
-
if (
|
|
647
|
+
if (isObject(schema) && !parseObject && !stack.includes(schema)) {
|
|
606
648
|
child.push(/*#__PURE__*/ jsx(renderer.ObjectCollapsible, {
|
|
607
649
|
name: "Attributes",
|
|
608
650
|
children: /*#__PURE__*/ jsx(Schema, {
|
|
@@ -611,7 +653,11 @@ function Schema({ name, schema, ctx }) {
|
|
|
611
653
|
ctx: {
|
|
612
654
|
...ctx,
|
|
613
655
|
parseObject: true,
|
|
614
|
-
required: false
|
|
656
|
+
required: false,
|
|
657
|
+
stack: [
|
|
658
|
+
schema,
|
|
659
|
+
...stack
|
|
660
|
+
]
|
|
615
661
|
}
|
|
616
662
|
})
|
|
617
663
|
}, "attributes"));
|
|
@@ -624,13 +670,13 @@ function Schema({ name, schema, ctx }) {
|
|
|
624
670
|
...schema.type === 'array' ? [
|
|
625
671
|
schema.items
|
|
626
672
|
] : []
|
|
627
|
-
].
|
|
673
|
+
].filter((s)=>isComplexType(s) && !stack.includes(s));
|
|
628
674
|
const renderedMentionedTypes = mentionedObjectTypes.map((s, idx)=>{
|
|
629
675
|
return /*#__PURE__*/ jsx(renderer.ObjectCollapsible, {
|
|
630
676
|
name: s.title ?? `Object ${(idx + 1).toString()}`,
|
|
631
677
|
children: /*#__PURE__*/ jsx(Schema, {
|
|
632
678
|
name: "element",
|
|
633
|
-
schema:
|
|
679
|
+
schema: s,
|
|
634
680
|
ctx: {
|
|
635
681
|
...ctx,
|
|
636
682
|
stack: [
|
|
@@ -659,70 +705,82 @@ function Schema({ name, schema, ctx }) {
|
|
|
659
705
|
if (schema.anyOf ?? schema.oneOf ?? schema.allOf) return true;
|
|
660
706
|
return isObject(schema) || schema.type === 'array';
|
|
661
707
|
}
|
|
662
|
-
function getSchemaType(schema, ctx) {
|
|
663
|
-
if (schema
|
|
664
|
-
const type = getSchemaType(
|
|
665
|
-
...schema,
|
|
666
|
-
nullable: false
|
|
667
|
-
}, ctx);
|
|
708
|
+
function getSchemaType(schema, ctx, isRoot = true) {
|
|
709
|
+
if (isNullable(schema) && isRoot) {
|
|
710
|
+
const type = getSchemaType(schema, ctx, false);
|
|
668
711
|
// null if schema only contains `nullable`
|
|
669
712
|
return type === 'unknown' ? 'null' : `${type} | null`;
|
|
670
713
|
}
|
|
671
714
|
if (schema.title) return schema.title;
|
|
672
|
-
if (schema.type === 'array') return `array<${getSchemaType(
|
|
673
|
-
if (schema.oneOf) return schema.oneOf.map((one)=>getSchemaType(
|
|
715
|
+
if (schema.type === 'array') return `array<${getSchemaType(schema.items, ctx)}>`;
|
|
716
|
+
if (schema.oneOf) return schema.oneOf.map((one)=>getSchemaType(one, ctx, false)).filter((v)=>v !== 'unknown').join(' | ');
|
|
674
717
|
if (schema.allOf) {
|
|
675
|
-
|
|
676
|
-
const hasNull = allTypeNames.includes('null');
|
|
677
|
-
const nonNullTypes = allTypeNames.filter((v)=>v !== 'null');
|
|
678
|
-
const nonNullTypeNames = nonNullTypes.join(' & ');
|
|
679
|
-
if (!hasNull) return nonNullTypeNames;
|
|
680
|
-
if (nonNullTypes.length === 0) return 'null';
|
|
681
|
-
else if (nonNullTypes.length === 1 || !hasNull) return `${nonNullTypeNames} | null`;
|
|
682
|
-
else return `(${nonNullTypeNames}) | null`;
|
|
718
|
+
return schema.allOf.map((one)=>getSchemaType(one, ctx, false)).filter((v)=>v !== 'unknown').join(' & ');
|
|
683
719
|
}
|
|
684
|
-
if (schema.not) return `not ${getSchemaType(
|
|
720
|
+
if (schema.not) return `not ${getSchemaType(schema.not, ctx, false)}`;
|
|
685
721
|
if (schema.anyOf) {
|
|
686
|
-
return `Any properties in ${schema.anyOf.map((one)=>getSchemaType(
|
|
722
|
+
return `Any properties in ${schema.anyOf.map((one)=>getSchemaType(one, ctx, false)).filter((v)=>v !== 'unknown').join(', ')}`;
|
|
687
723
|
}
|
|
688
724
|
if (schema.type === 'string' && schema.format === 'binary' && ctx.allowFile) return 'file';
|
|
689
|
-
if (schema.type
|
|
725
|
+
if (schema.type && Array.isArray(schema.type)) {
|
|
726
|
+
const nonNullTypes = schema.type.filter((v)=>v !== 'null');
|
|
727
|
+
if (nonNullTypes.length > 0) return nonNullTypes.join(' | ');
|
|
728
|
+
} else if (schema.type && schema.type !== 'null') {
|
|
729
|
+
return schema.type;
|
|
730
|
+
}
|
|
690
731
|
if (isObject(schema)) return 'object';
|
|
691
732
|
return 'unknown';
|
|
692
733
|
}
|
|
693
734
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
735
|
+
/**
|
|
736
|
+
* Summarize method endpoint information
|
|
737
|
+
*/ function createMethod(method, path, operation) {
|
|
738
|
+
return {
|
|
739
|
+
description: path.description,
|
|
740
|
+
summary: path.summary,
|
|
741
|
+
...operation,
|
|
742
|
+
parameters: [
|
|
743
|
+
...operation.parameters ?? [],
|
|
744
|
+
...path.parameters ?? []
|
|
745
|
+
],
|
|
746
|
+
method: method.toUpperCase()
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
const methodKeys = [
|
|
751
|
+
'get',
|
|
752
|
+
'post',
|
|
753
|
+
'patch',
|
|
754
|
+
'delete',
|
|
755
|
+
'head',
|
|
756
|
+
'put'
|
|
757
|
+
];
|
|
758
|
+
|
|
759
|
+
function Operation({ type = 'operation', path, method, ctx, hasHead, headingLevel = 2 }) {
|
|
760
|
+
const { baseUrls } = ctx;
|
|
761
|
+
const body = method.requestBody;
|
|
697
762
|
const security = method.security ?? ctx.document.security;
|
|
698
|
-
|
|
763
|
+
let headNode = null;
|
|
764
|
+
let bodyNode = null;
|
|
765
|
+
let callbacksNode = null;
|
|
699
766
|
if (hasHead) {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
method: method,
|
|
711
|
-
ctx: ctx
|
|
712
|
-
}, "playground"));
|
|
713
|
-
if (security) {
|
|
714
|
-
info.push(heading(level, 'Authorization', ctx));
|
|
715
|
-
info.push(/*#__PURE__*/ jsx(AuthSection, {
|
|
716
|
-
requirements: security,
|
|
717
|
-
ctx: ctx
|
|
718
|
-
}, "auth"));
|
|
767
|
+
const title = method.summary ?? (method.operationId ? idToTitle(method.operationId) : path);
|
|
768
|
+
headNode = /*#__PURE__*/ jsxs(Fragment, {
|
|
769
|
+
children: [
|
|
770
|
+
heading(headingLevel, title, ctx),
|
|
771
|
+
method.description ? /*#__PURE__*/ jsx(Markdown, {
|
|
772
|
+
text: method.description
|
|
773
|
+
}, "description") : null
|
|
774
|
+
]
|
|
775
|
+
});
|
|
776
|
+
headingLevel++;
|
|
719
777
|
}
|
|
720
778
|
if (body) {
|
|
721
779
|
const type = getPreferredType(body.content);
|
|
722
780
|
if (!type) throw new Error(`No supported media type for body content: ${path}`);
|
|
723
|
-
|
|
781
|
+
bodyNode = /*#__PURE__*/ jsxs(Fragment, {
|
|
724
782
|
children: [
|
|
725
|
-
heading(
|
|
783
|
+
heading(headingLevel, 'Request Body', ctx),
|
|
726
784
|
/*#__PURE__*/ jsxs("div", {
|
|
727
785
|
className: "mb-8 flex flex-row items-center justify-between gap-2",
|
|
728
786
|
children: [
|
|
@@ -739,7 +797,7 @@ function Operation({ baseUrls, path, method, ctx, hasHead }) {
|
|
|
739
797
|
}) : null,
|
|
740
798
|
/*#__PURE__*/ jsx(Schema, {
|
|
741
799
|
name: "body",
|
|
742
|
-
schema:
|
|
800
|
+
schema: body.content[type].schema ?? {},
|
|
743
801
|
ctx: {
|
|
744
802
|
readOnly: method.method === 'GET',
|
|
745
803
|
writeOnly: method.method !== 'GET',
|
|
@@ -749,11 +807,11 @@ function Operation({ baseUrls, path, method, ctx, hasHead }) {
|
|
|
749
807
|
}
|
|
750
808
|
})
|
|
751
809
|
]
|
|
752
|
-
}
|
|
810
|
+
});
|
|
753
811
|
}
|
|
754
812
|
const parameterGroups = new Map();
|
|
755
813
|
const endpoint = generateSample(path, method, ctx);
|
|
756
|
-
for (const param of method.parameters){
|
|
814
|
+
for (const param of method.parameters ?? []){
|
|
757
815
|
const pInfo = endpoint.parameters.find((item)=>item.name === param.name && item.in === param.in);
|
|
758
816
|
if (!pInfo) continue;
|
|
759
817
|
const schema = pInfo.schema;
|
|
@@ -781,24 +839,81 @@ function Operation({ baseUrls, path, method, ctx, hasHead }) {
|
|
|
781
839
|
}, param.name));
|
|
782
840
|
parameterGroups.set(groupName, group);
|
|
783
841
|
}
|
|
784
|
-
|
|
785
|
-
|
|
842
|
+
if (method.callbacks) {
|
|
843
|
+
callbacksNode = /*#__PURE__*/ jsxs(Fragment, {
|
|
844
|
+
children: [
|
|
845
|
+
heading(headingLevel, 'Webhooks', ctx),
|
|
846
|
+
Object.entries(method.callbacks).map(([name, callback])=>{
|
|
847
|
+
const nodes = Object.entries(callback).map(([path, pathItem])=>{
|
|
848
|
+
const pathNodes = methodKeys.map((method)=>{
|
|
849
|
+
const operation = pathItem[method];
|
|
850
|
+
if (!operation) return null;
|
|
851
|
+
return /*#__PURE__*/ jsx(Operation, {
|
|
852
|
+
type: "webhook",
|
|
853
|
+
hasHead: true,
|
|
854
|
+
path: path,
|
|
855
|
+
headingLevel: headingLevel + 1,
|
|
856
|
+
method: createMethod(method, pathItem, operation),
|
|
857
|
+
ctx: ctx
|
|
858
|
+
}, method);
|
|
859
|
+
});
|
|
860
|
+
return /*#__PURE__*/ jsx(Fragment$1, {
|
|
861
|
+
children: pathNodes
|
|
862
|
+
}, path);
|
|
863
|
+
});
|
|
864
|
+
return /*#__PURE__*/ jsx(Fragment$1, {
|
|
865
|
+
children: nodes
|
|
866
|
+
}, name);
|
|
867
|
+
})
|
|
868
|
+
]
|
|
869
|
+
});
|
|
786
870
|
}
|
|
787
|
-
|
|
871
|
+
const info = /*#__PURE__*/ jsxs(ctx.renderer.APIInfo, {
|
|
872
|
+
head: headNode,
|
|
873
|
+
method: method.method,
|
|
874
|
+
route: path,
|
|
875
|
+
baseUrls: type === 'operation' ? baseUrls : [],
|
|
788
876
|
children: [
|
|
789
|
-
/*#__PURE__*/ jsx(
|
|
790
|
-
|
|
791
|
-
route: path,
|
|
792
|
-
baseUrls: baseUrls,
|
|
793
|
-
children: info
|
|
794
|
-
}),
|
|
795
|
-
/*#__PURE__*/ jsx(APIExample, {
|
|
877
|
+
type === 'operation' ? /*#__PURE__*/ jsx(Playground, {
|
|
878
|
+
path: path,
|
|
796
879
|
method: method,
|
|
797
|
-
endpoint: endpoint,
|
|
798
880
|
ctx: ctx
|
|
799
|
-
})
|
|
881
|
+
}) : null,
|
|
882
|
+
security ? /*#__PURE__*/ jsxs(Fragment, {
|
|
883
|
+
children: [
|
|
884
|
+
heading(headingLevel, 'Authorization', ctx),
|
|
885
|
+
/*#__PURE__*/ jsx(AuthSection, {
|
|
886
|
+
requirements: security,
|
|
887
|
+
ctx: ctx
|
|
888
|
+
})
|
|
889
|
+
]
|
|
890
|
+
}) : null,
|
|
891
|
+
bodyNode,
|
|
892
|
+
Array.from(parameterGroups.entries()).map(([group, params])=>{
|
|
893
|
+
return /*#__PURE__*/ jsxs(Fragment$1, {
|
|
894
|
+
children: [
|
|
895
|
+
heading(headingLevel, group, ctx),
|
|
896
|
+
params
|
|
897
|
+
]
|
|
898
|
+
}, group);
|
|
899
|
+
}),
|
|
900
|
+
callbacksNode
|
|
800
901
|
]
|
|
801
902
|
});
|
|
903
|
+
if (type === 'operation') {
|
|
904
|
+
return /*#__PURE__*/ jsxs(ctx.renderer.API, {
|
|
905
|
+
children: [
|
|
906
|
+
info,
|
|
907
|
+
/*#__PURE__*/ jsx(APIExample, {
|
|
908
|
+
method: method,
|
|
909
|
+
endpoint: endpoint,
|
|
910
|
+
ctx: ctx
|
|
911
|
+
})
|
|
912
|
+
]
|
|
913
|
+
});
|
|
914
|
+
} else {
|
|
915
|
+
return info;
|
|
916
|
+
}
|
|
802
917
|
}
|
|
803
918
|
const defaultSamples = [
|
|
804
919
|
{
|
|
@@ -875,6 +990,14 @@ function AuthSection({ ctx: { document, renderer }, requirements }) {
|
|
|
875
990
|
for (const requirement of requirements){
|
|
876
991
|
for (const schema of getSecurities(requirement, document)){
|
|
877
992
|
const prefix = getSecurityPrefix(schema);
|
|
993
|
+
const scopeElement = schema.scopes.length > 0 ? /*#__PURE__*/ jsxs("p", {
|
|
994
|
+
children: [
|
|
995
|
+
"Scope: ",
|
|
996
|
+
/*#__PURE__*/ jsx("code", {
|
|
997
|
+
children: schema.scopes.join(', ')
|
|
998
|
+
})
|
|
999
|
+
]
|
|
1000
|
+
}) : null;
|
|
878
1001
|
if (schema.type === 'http') {
|
|
879
1002
|
info.push(/*#__PURE__*/ jsxs(renderer.Property, {
|
|
880
1003
|
name: "Authorization",
|
|
@@ -889,7 +1012,8 @@ function AuthSection({ ctx: { document, renderer }, requirements }) {
|
|
|
889
1012
|
"In: ",
|
|
890
1013
|
/*#__PURE__*/ jsx("code", {
|
|
891
1014
|
children: "header"
|
|
892
|
-
})
|
|
1015
|
+
}),
|
|
1016
|
+
scopeElement
|
|
893
1017
|
]
|
|
894
1018
|
})
|
|
895
1019
|
]
|
|
@@ -912,14 +1036,7 @@ function AuthSection({ ctx: { document, renderer }, requirements }) {
|
|
|
912
1036
|
})
|
|
913
1037
|
]
|
|
914
1038
|
}),
|
|
915
|
-
|
|
916
|
-
children: [
|
|
917
|
-
"Scope: ",
|
|
918
|
-
/*#__PURE__*/ jsx("code", {
|
|
919
|
-
children: schema.scopes.join(', ')
|
|
920
|
-
})
|
|
921
|
-
]
|
|
922
|
-
}) : null
|
|
1039
|
+
scopeElement
|
|
923
1040
|
]
|
|
924
1041
|
}, id++));
|
|
925
1042
|
}
|
|
@@ -936,32 +1053,37 @@ function AuthSection({ ctx: { document, renderer }, requirements }) {
|
|
|
936
1053
|
"In: ",
|
|
937
1054
|
/*#__PURE__*/ jsx("code", {
|
|
938
1055
|
children: schema.in
|
|
939
|
-
})
|
|
1056
|
+
}),
|
|
1057
|
+
scopeElement
|
|
940
1058
|
]
|
|
941
1059
|
})
|
|
942
1060
|
]
|
|
943
1061
|
}, id++));
|
|
944
1062
|
}
|
|
945
1063
|
if (schema.type === 'openIdConnect') {
|
|
946
|
-
info.push(/*#__PURE__*/
|
|
1064
|
+
info.push(/*#__PURE__*/ jsxs(renderer.Property, {
|
|
947
1065
|
name: "OpenID Connect",
|
|
948
1066
|
type: "<token>",
|
|
949
1067
|
required: true,
|
|
950
|
-
children:
|
|
951
|
-
|
|
952
|
-
|
|
1068
|
+
children: [
|
|
1069
|
+
schema.description ? /*#__PURE__*/ jsx(Markdown, {
|
|
1070
|
+
text: schema.description
|
|
1071
|
+
}) : null,
|
|
1072
|
+
scopeElement
|
|
1073
|
+
]
|
|
953
1074
|
}, id++));
|
|
954
1075
|
}
|
|
955
1076
|
}
|
|
956
1077
|
}
|
|
957
1078
|
return info;
|
|
958
1079
|
}
|
|
959
|
-
async function ResponseTabs({ endpoint, operation, ctx: { renderer, generateTypeScriptSchema } }) {
|
|
1080
|
+
async function ResponseTabs({ endpoint, operation, ctx: { renderer, generateTypeScriptSchema, dereferenceMap } }) {
|
|
960
1081
|
const items = [];
|
|
961
1082
|
const children = [];
|
|
1083
|
+
if (!operation.responses) return null;
|
|
962
1084
|
for (const code of Object.keys(operation.responses)){
|
|
963
1085
|
const types = [];
|
|
964
|
-
let description =
|
|
1086
|
+
let description = operation.responses[code].description;
|
|
965
1087
|
if (!description && code in endpoint.responses) description = endpoint.responses[code].schema.description ?? '';
|
|
966
1088
|
if (code in endpoint.responses) {
|
|
967
1089
|
types.push({
|
|
@@ -974,7 +1096,7 @@ async function ResponseTabs({ endpoint, operation, ctx: { renderer, generateType
|
|
|
974
1096
|
if (generateTypeScriptSchema) {
|
|
975
1097
|
ts = await generateTypeScriptSchema(endpoint, code);
|
|
976
1098
|
} else if (generateTypeScriptSchema === undefined) {
|
|
977
|
-
ts = await getTypescriptSchema(endpoint, code);
|
|
1099
|
+
ts = await getTypescriptSchema(endpoint, code, dereferenceMap);
|
|
978
1100
|
}
|
|
979
1101
|
if (ts) {
|
|
980
1102
|
types.push({
|
|
@@ -1005,21 +1127,6 @@ async function ResponseTabs({ endpoint, operation, ctx: { renderer, generateType
|
|
|
1005
1127
|
});
|
|
1006
1128
|
}
|
|
1007
1129
|
|
|
1008
|
-
/**
|
|
1009
|
-
* Summarize method endpoint information
|
|
1010
|
-
*/ function createMethod(method, path, operation) {
|
|
1011
|
-
return {
|
|
1012
|
-
description: path.description,
|
|
1013
|
-
summary: path.summary,
|
|
1014
|
-
...operation,
|
|
1015
|
-
parameters: [
|
|
1016
|
-
...noRef(operation.parameters ?? []),
|
|
1017
|
-
...noRef(path.parameters ?? [])
|
|
1018
|
-
],
|
|
1019
|
-
method: method.toUpperCase()
|
|
1020
|
-
};
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
1130
|
async function CodeBlock({ code, lang, options, ...rest }) {
|
|
1024
1131
|
const rendered = await highlight(code, {
|
|
1025
1132
|
lang,
|
|
@@ -1082,38 +1189,79 @@ function createRenders(shikiOptions) {
|
|
|
1082
1189
|
}
|
|
1083
1190
|
|
|
1084
1191
|
const cache = new Map();
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1192
|
+
/**
|
|
1193
|
+
* process & reference input document to a Fumadocs OpenAPI compatible format
|
|
1194
|
+
*/ async function processDocument(document, disableCache = false) {
|
|
1195
|
+
const cached = !disableCache && typeof document === 'string' ? cache.get(document) : null;
|
|
1196
|
+
if (cached) return cached;
|
|
1197
|
+
let bundled = await Parser.bundle(document, {
|
|
1198
|
+
mutateInputSchema: false
|
|
1199
|
+
});
|
|
1200
|
+
bundled = upgrade(bundled).specification;
|
|
1201
|
+
const dereferenceMap = new Map();
|
|
1202
|
+
const dereferenced = await Parser.dereference(bundled, {
|
|
1203
|
+
mutateInputSchema: true,
|
|
1204
|
+
dereference: {
|
|
1205
|
+
onDereference ($ref, schema) {
|
|
1206
|
+
dereferenceMap.set(schema, $ref);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
});
|
|
1210
|
+
const processed = {
|
|
1211
|
+
document: dereferenced,
|
|
1212
|
+
dereferenceMap
|
|
1213
|
+
};
|
|
1214
|
+
if (!disableCache && typeof document === 'string') {
|
|
1215
|
+
cache.set(document, processed);
|
|
1094
1216
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1217
|
+
return processed;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
async function APIPage(props) {
|
|
1221
|
+
const { operations, hasHead = true, webhooks, disableCache = process.env.NODE_ENV === 'development' } = props;
|
|
1222
|
+
const processed = await processDocument(props.document, disableCache);
|
|
1223
|
+
const ctx = await getContext(processed, props);
|
|
1224
|
+
const { document } = processed;
|
|
1225
|
+
return /*#__PURE__*/ jsxs(ctx.renderer.Root, {
|
|
1097
1226
|
baseUrl: ctx.baseUrl,
|
|
1098
|
-
children:
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1227
|
+
children: [
|
|
1228
|
+
operations?.map((item)=>{
|
|
1229
|
+
const pathItem = document.paths?.[item.path];
|
|
1230
|
+
if (!pathItem) return null;
|
|
1231
|
+
const operation = pathItem[item.method];
|
|
1232
|
+
if (!operation) return null;
|
|
1233
|
+
const method = createMethod(item.method, pathItem, operation);
|
|
1234
|
+
return /*#__PURE__*/ jsx(Operation, {
|
|
1235
|
+
method: method,
|
|
1236
|
+
path: item.path,
|
|
1237
|
+
ctx: ctx,
|
|
1238
|
+
hasHead: hasHead
|
|
1239
|
+
}, `${item.path}:${item.method}`);
|
|
1240
|
+
}),
|
|
1241
|
+
webhooks?.map((item)=>{
|
|
1242
|
+
const webhook = document.webhooks?.[item.name];
|
|
1243
|
+
if (!webhook) return;
|
|
1244
|
+
const hook = webhook[item.method];
|
|
1245
|
+
if (!hook) return;
|
|
1246
|
+
const method = createMethod(item.method, webhook, hook);
|
|
1247
|
+
return /*#__PURE__*/ jsx(Operation, {
|
|
1248
|
+
type: "webhook",
|
|
1249
|
+
method: method,
|
|
1250
|
+
ctx: {
|
|
1251
|
+
...ctx,
|
|
1252
|
+
baseUrl: 'http://localhost:8080'
|
|
1253
|
+
},
|
|
1254
|
+
path: `/${item.name}`,
|
|
1255
|
+
hasHead: hasHead
|
|
1256
|
+
}, `${item.name}:${item.method}`);
|
|
1257
|
+
})
|
|
1258
|
+
]
|
|
1112
1259
|
});
|
|
1113
1260
|
}
|
|
1114
|
-
function getContext(document, options) {
|
|
1261
|
+
async function getContext({ document, dereferenceMap }, options = {}) {
|
|
1115
1262
|
return {
|
|
1116
|
-
document,
|
|
1263
|
+
document: document,
|
|
1264
|
+
dereferenceMap,
|
|
1117
1265
|
renderer: {
|
|
1118
1266
|
...createRenders(options.shikiOptions),
|
|
1119
1267
|
...options.renderer
|
|
@@ -1122,6 +1270,7 @@ function getContext(document, options) {
|
|
|
1122
1270
|
generateTypeScriptSchema: options.generateTypeScriptSchema,
|
|
1123
1271
|
generateCodeSamples: options.generateCodeSamples,
|
|
1124
1272
|
baseUrl: document.servers?.[0].url ?? 'https://example.com',
|
|
1273
|
+
baseUrls: document.servers?.map((s)=>s.url) ?? [],
|
|
1125
1274
|
slugger: new Slugger()
|
|
1126
1275
|
};
|
|
1127
1276
|
}
|