fumadocs-openapi 4.4.2 → 5.0.1

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.
@@ -0,0 +1,1094 @@
1
+ import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
2
+ import Parser from '@apidevtools/json-schema-ref-parser';
3
+ import Slugger from 'github-slugger';
4
+ import { createElement, Fragment, useMemo } from 'react';
5
+ import { sample } from 'openapi-sampler';
6
+ import { compile } from 'json-schema-to-typescript';
7
+ import { createProcessor } from '@mdx-js/mdx';
8
+ import { remarkGfm, rehypeCode } from 'fumadocs-core/mdx-plugins';
9
+ import defaultMdxComponents from 'fumadocs-ui/mdx';
10
+ import { Heading } from 'fumadocs-ui/components/heading';
11
+ import { Tabs, Tab } from 'fumadocs-ui/components/tabs';
12
+ import { Accordions, Accordion } from 'fumadocs-ui/components/accordion';
13
+ import * as Base from 'fumadocs-ui/components/codeblock';
14
+ import { createHighlighter, bundledThemes, bundledLanguages } from 'shiki';
15
+ import { Root, API, APIInfo, APIExample as APIExample$1, Property, ObjectCollapsible, APIPlayground } from '../ui/index.js';
16
+ import { cva } from 'class-variance-authority';
17
+
18
+ function noRef(v) {
19
+ return v;
20
+ }
21
+ function getPreferredType(body) {
22
+ if ('application/json' in body) return 'application/json';
23
+ return Object.keys(body)[0];
24
+ }
25
+ /**
26
+ * Convert to JSON string if necessary
27
+ */ function toSampleInput(value) {
28
+ return typeof value === 'string' ? value : JSON.stringify(value, null, 2);
29
+ }
30
+
31
+ function generateSample(path, method, baseUrl) {
32
+ const params = [];
33
+ const responses = {};
34
+ for (const param of method.parameters){
35
+ if (param.schema) {
36
+ params.push({
37
+ name: param.name,
38
+ in: param.in,
39
+ schema: noRef(param.schema),
40
+ sample: param.example ?? sample(param.schema)
41
+ });
42
+ } else if (param.content) {
43
+ const key = getPreferredType(param.content);
44
+ const content = key ? param.content[key] : undefined;
45
+ if (!key || !content?.schema) throw new Error(`Cannot find parameter schema for ${param.name} in ${path} ${method.method}`);
46
+ params.push({
47
+ name: param.name,
48
+ in: param.in,
49
+ schema: noRef(content.schema),
50
+ sample: content.example ?? param.example ?? sample(content.schema)
51
+ });
52
+ }
53
+ }
54
+ let bodyOutput;
55
+ if (method.requestBody) {
56
+ const body = noRef(method.requestBody).content;
57
+ const type = getPreferredType(body);
58
+ const schema = type ? noRef(body[type].schema) : undefined;
59
+ if (!type || !schema) throw new Error(`Cannot find body schema for ${path} ${method.method}`);
60
+ bodyOutput = {
61
+ schema,
62
+ mediaType: type,
63
+ sample: body[type].example ?? generateBody(method.method, schema)
64
+ };
65
+ }
66
+ for (const [code, value] of Object.entries(method.responses)){
67
+ const content = noRef(value).content;
68
+ if (!content) continue;
69
+ const mediaType = getPreferredType(content);
70
+ if (!mediaType) continue;
71
+ const responseSchema = noRef(content[mediaType].schema);
72
+ if (!responseSchema) continue;
73
+ responses[code] = {
74
+ mediaType,
75
+ sample: content[mediaType].example ?? generateBody(method.method, responseSchema),
76
+ schema: responseSchema
77
+ };
78
+ }
79
+ let pathWithParameters = path;
80
+ const queryParams = new URLSearchParams();
81
+ for (const param of params){
82
+ const value = generateBody(method.method, param.schema);
83
+ if (param.in === 'query') queryParams.append(param.name, toSampleInput(value));
84
+ if (param.in === 'path') pathWithParameters = pathWithParameters.replace(`{${param.name}}`, toSampleInput(value));
85
+ }
86
+ if (queryParams.size > 0) pathWithParameters = `${pathWithParameters}?${queryParams.toString()}`;
87
+ return {
88
+ url: new URL(`${baseUrl}${pathWithParameters}`).toString(),
89
+ body: bodyOutput,
90
+ responses,
91
+ method: method.method,
92
+ parameters: params
93
+ };
94
+ }
95
+ function generateBody(method, schema) {
96
+ return sample(schema, {
97
+ skipReadOnly: method !== 'GET',
98
+ skipWriteOnly: method === 'GET'
99
+ });
100
+ }
101
+
102
+ function getSampleRequest$2(endpoint) {
103
+ const s = [];
104
+ s.push(`curl -X ${endpoint.method} "${endpoint.url}"`);
105
+ for (const param of endpoint.parameters){
106
+ if (param.in === 'header') {
107
+ const header = `${param.name}: ${toSampleInput(param.sample)}`;
108
+ s.push(`-H "${header}"`);
109
+ }
110
+ if (param.in === 'cookie') {
111
+ const cookie = JSON.stringify(`${param.name}=${toSampleInput(param.sample)}`);
112
+ s.push(`--cookie ${cookie}`);
113
+ }
114
+ }
115
+ if (endpoint.body?.mediaType === 'multipart/form-data') {
116
+ const sample = endpoint.body.sample;
117
+ if (sample && typeof sample === 'object') {
118
+ for (const [key, value] of Object.entries(sample)){
119
+ s.push(`-F ${key}=${JSON.stringify(value, null, 2)}`);
120
+ }
121
+ }
122
+ } else if (endpoint.body) {
123
+ s.push(`-H "Content-Type: application/json"`);
124
+ s.push(`-d '${toSampleInput(endpoint.body.sample)}'`);
125
+ }
126
+ return s.flatMap((v, i)=>v.split('\n').map((line)=>i > 0 ? ` ${line}` : line).join('\n')).join(' \\\n');
127
+ }
128
+
129
+ function getSampleRequest$1(endpoint) {
130
+ const s = [];
131
+ const options = new Map();
132
+ const headers = new Map();
133
+ const cookies = new Map();
134
+ for (const param of endpoint.parameters){
135
+ if (param.in === 'header') {
136
+ headers.set(param.name, param.sample);
137
+ }
138
+ if (param.in === 'cookie') {
139
+ cookies.set(param.name, param.sample);
140
+ }
141
+ }
142
+ if (cookies.size > 0) {
143
+ headers.set('cookie', Array.from(cookies.entries()).map(([key, value])=>`${key}=${toSampleInput(value)}`).join('; '));
144
+ }
145
+ if (headers.size > 0) {
146
+ options.set('headers', JSON.stringify(Object.fromEntries(headers.entries()), undefined, 2).replaceAll('\n', '\n '));
147
+ }
148
+ if (endpoint.body?.mediaType === 'multipart/form-data' && typeof endpoint.body.sample === 'object' && endpoint.body.sample) {
149
+ s.push(`const formData = new FormData();`);
150
+ for (const [key, value] of Object.entries(endpoint.body.sample))s.push(`formData.set(${key}, ${JSON.stringify(value)})`);
151
+ options.set('body', 'formData');
152
+ } else if (endpoint.body) {
153
+ options.set('body', `JSON.stringify(${JSON.stringify(endpoint.body.sample, null, 2).replaceAll('\n', '\n ')})`);
154
+ }
155
+ const optionsStr = Array.from(options.entries()).map(([k, v])=>` ${k}: ${v}`).join(',\n');
156
+ s.push(`fetch(${JSON.stringify(endpoint.url)}, {\n${optionsStr}\n});`);
157
+ return s.join('\n\n');
158
+ }
159
+
160
+ function getSampleRequest(endpoint) {
161
+ const imports = [
162
+ 'fmt',
163
+ 'net/http',
164
+ 'io/ioutil'
165
+ ];
166
+ const headers = new Map();
167
+ const cookies = new Map();
168
+ const variables = new Map();
169
+ // additional lines before initializing request
170
+ const additional = [];
171
+ for (const p of endpoint.parameters){
172
+ if (p.in === 'header') headers.set(p.name, JSON.stringify(p.sample));
173
+ if (p.in === 'cookie') cookies.set(p.name, toSampleInput(p.sample));
174
+ }
175
+ variables.set('url', JSON.stringify(endpoint.url));
176
+ if (cookies.size > 0) headers.set('Cookie', JSON.stringify(Array.from(cookies.entries()).map(([key, value])=>`${key}=${value}`).join('; ')));
177
+ if (endpoint.body) {
178
+ headers.set('Content-Type', `"${endpoint.body.mediaType}"`);
179
+ if (endpoint.body.mediaType === 'application/json') {
180
+ imports.push('strings');
181
+ variables.set('payload', `strings.NewReader(\`${JSON.stringify(endpoint.body.sample, null, 2).replaceAll('\n', '\n ')}\`)`);
182
+ }
183
+ if (endpoint.body.mediaType === 'multipart/form-data' && typeof endpoint.body.sample === 'object') {
184
+ imports.push('mime/multipart', 'bytes');
185
+ variables.set('payload', `new(bytes.Buffer)`);
186
+ variables.set('mp', 'multipart.NewWriter(payload)');
187
+ for (const [key, value] of Object.entries(endpoint.body.sample ?? {})){
188
+ additional.push(`mp.WriteField("${key}", ${JSON.stringify(toSampleInput(value))})`);
189
+ }
190
+ }
191
+ }
192
+ return `package main
193
+
194
+ import (
195
+ ${imports.map((v)=>` "${v}"`).join('\n')}
196
+ )
197
+
198
+ func main() {
199
+ ${Array.from(variables.entries()).map(([k, v])=>` ${k} := ${v}`).join('\n')}
200
+ ${additional.join('\n ')}
201
+ req, _ := http.NewRequest("${endpoint.method}", url, ${variables.has('payload') ? 'payload' : 'nil'})
202
+ ${Array.from(headers.entries()).map(([key, value])=>`req.Header.Add("${key}", ${value})`).join('\n')}
203
+ res, _ := http.DefaultClient.Do(req)
204
+ defer res.Body.Close()
205
+ body, _ := ioutil.ReadAll(res.Body)
206
+
207
+ fmt.Println(res)
208
+ fmt.Println(string(body))
209
+ }`;
210
+ }
211
+
212
+ async function getTypescriptSchema(endpoint, code) {
213
+ if (code in endpoint.responses) {
214
+ return compile(endpoint.responses[code].schema, 'Response', {
215
+ bannerComment: '',
216
+ additionalProperties: false,
217
+ format: true,
218
+ enableConstEnums: false
219
+ });
220
+ }
221
+ }
222
+
223
+ function getScheme(requirement, document) {
224
+ const results = [];
225
+ const schemas = document.components?.securitySchemes ?? {};
226
+ for (const [key, scopes] of Object.entries(requirement)){
227
+ if (!(key in schemas)) return [];
228
+ const schema = noRef(schemas[key]);
229
+ results.push({
230
+ ...schema,
231
+ scopes
232
+ });
233
+ }
234
+ return results;
235
+ }
236
+
237
+ function Playground({ path, method, ctx }) {
238
+ let currentId = 0;
239
+ const bodyContent = noRef(method.requestBody)?.content;
240
+ const mediaType = bodyContent ? getPreferredType(bodyContent) : undefined;
241
+ const context = {
242
+ allowFile: mediaType === 'multipart/form-data',
243
+ schema: {},
244
+ nextId () {
245
+ return String(currentId++);
246
+ },
247
+ registered: new WeakMap()
248
+ };
249
+ const props = {
250
+ authorization: getAuthorizationField(method, ctx),
251
+ method: method.method,
252
+ route: path,
253
+ bodyType: mediaType === 'multipart/form-data' ? 'form-data' : 'json',
254
+ path: method.parameters.filter((v)=>v.in === 'path').map((v)=>parameterToField(v, context)),
255
+ query: method.parameters.filter((v)=>v.in === 'query').map((v)=>parameterToField(v, context)),
256
+ header: method.parameters.filter((v)=>v.in === 'header').map((v)=>parameterToField(v, context)),
257
+ body: bodyContent && mediaType && bodyContent[mediaType].schema ? toSchema(noRef(bodyContent[mediaType].schema), true, context) : undefined,
258
+ schemas: context.schema
259
+ };
260
+ return /*#__PURE__*/ jsx(ctx.renderer.APIPlayground, {
261
+ ...props
262
+ });
263
+ }
264
+ function getAuthorizationField(method, ctx) {
265
+ const security = method.security ?? ctx.document.security ?? [];
266
+ if (security.length === 0) return;
267
+ const singular = security.find((requirements)=>Object.keys(requirements).length === 1);
268
+ if (!singular) return;
269
+ const scheme = getScheme(singular, ctx.document)[0];
270
+ return {
271
+ type: 'string',
272
+ name: 'Authorization',
273
+ defaultValue: scheme.type === 'oauth2' || scheme.type === 'http' && scheme.scheme === 'bearer' ? 'Bearer' : 'Basic',
274
+ isRequired: security.every((requirements)=>Object.keys(requirements).length > 0),
275
+ description: 'The Authorization access token'
276
+ };
277
+ }
278
+ function getIdFromSchema(schema, required, ctx) {
279
+ const registered = ctx.registered.get(schema);
280
+ if (registered === undefined) {
281
+ const id = ctx.nextId();
282
+ ctx.registered.set(schema, id);
283
+ ctx.schema[id] = toSchema(schema, required, ctx);
284
+ return id;
285
+ }
286
+ return registered;
287
+ }
288
+ function parameterToField(v, ctx) {
289
+ return {
290
+ name: v.name,
291
+ ...toSchema(noRef(v.schema) ?? {
292
+ type: 'string'
293
+ }, v.required ?? false, ctx)
294
+ };
295
+ }
296
+ function toReference(schema, required, ctx) {
297
+ return {
298
+ type: 'ref',
299
+ isRequired: required,
300
+ schema: getIdFromSchema(schema, false, ctx)
301
+ };
302
+ }
303
+ function toSchema(schema, required, ctx) {
304
+ if (schema.type === 'array') {
305
+ return {
306
+ type: 'array',
307
+ description: schema.description ?? schema.title,
308
+ isRequired: required,
309
+ items: getIdFromSchema(noRef(schema.items), false, ctx)
310
+ };
311
+ }
312
+ if (schema.type === 'object' || schema.properties !== undefined || schema.allOf !== undefined) {
313
+ const properties = {};
314
+ Object.entries(schema.properties ?? {}).forEach(([key, prop])=>{
315
+ properties[key] = toReference(noRef(prop), schema.required?.includes(key) ?? false, ctx);
316
+ });
317
+ schema.allOf?.forEach((c)=>{
318
+ const field = toSchema(noRef(c), true, ctx);
319
+ if (field.type === 'object') Object.assign(properties, field.properties);
320
+ });
321
+ const additional = noRef(schema.additionalProperties);
322
+ let additionalProperties;
323
+ if (additional && typeof additional === 'object') {
324
+ if (!additional.type && !additional.anyOf && !additional.allOf && !additional.oneOf) {
325
+ additionalProperties = true;
326
+ } else {
327
+ additionalProperties = getIdFromSchema(additional, false, ctx);
328
+ }
329
+ } else {
330
+ additionalProperties = additional;
331
+ }
332
+ return {
333
+ type: 'object',
334
+ isRequired: required,
335
+ description: schema.description ?? schema.title,
336
+ properties,
337
+ additionalProperties
338
+ };
339
+ }
340
+ if (schema.type === undefined) {
341
+ const combine = schema.anyOf ?? schema.oneOf;
342
+ if (combine) {
343
+ return {
344
+ type: 'switcher',
345
+ description: schema.description ?? schema.title,
346
+ items: Object.fromEntries(combine.map((c, idx)=>{
347
+ const item = noRef(c);
348
+ return [
349
+ item.title ?? item.type ?? `Item ${idx.toString()}`,
350
+ toReference(item, true, ctx)
351
+ ];
352
+ })),
353
+ isRequired: required
354
+ };
355
+ }
356
+ return {
357
+ type: 'null',
358
+ isRequired: false
359
+ };
360
+ }
361
+ if (ctx.allowFile && schema.type === 'string' && schema.format === 'binary') {
362
+ return {
363
+ type: 'file',
364
+ isRequired: required,
365
+ description: schema.description ?? schema.title
366
+ };
367
+ }
368
+ return {
369
+ type: schema.type === 'integer' ? 'number' : schema.type,
370
+ defaultValue: schema.example ?? '',
371
+ isRequired: required,
372
+ description: schema.description ?? schema.title
373
+ };
374
+ }
375
+
376
+ function idToTitle(id) {
377
+ let result = [];
378
+ for (const c of id){
379
+ if (result.length === 0) result.push(c.toLocaleUpperCase());
380
+ else if (c === '.') result = [];
381
+ else if (/^[A-Z]$/.test(c) && result.at(-1) !== ' ') result.push(' ', c);
382
+ else if (c === '-') result.push(' ');
383
+ else result.push(c);
384
+ }
385
+ return result.join('');
386
+ }
387
+
388
+ const processor = createProcessor({
389
+ remarkPlugins: [
390
+ remarkGfm
391
+ ],
392
+ rehypePlugins: [
393
+ rehypeCode
394
+ ],
395
+ outputFormat: 'function-body',
396
+ development: process.env.NODE_ENV === 'development'
397
+ });
398
+ async function Markdown({ text }) {
399
+ const result = await processor.process({
400
+ value: text
401
+ });
402
+ const jsxRuntime = process.env.NODE_ENV === 'development' ? await import('react/jsx-dev-runtime') : await import('react/jsx-runtime');
403
+ const fullScope = {
404
+ opts: jsxRuntime
405
+ };
406
+ const keys = Object.keys(fullScope);
407
+ const values = Object.values(fullScope);
408
+ const hydrateFn = Reflect.construct(Function, keys.concat(String(result.value)));
409
+ const rendered = hydrateFn.apply(hydrateFn, values);
410
+ return /*#__PURE__*/ createElement(rendered.default, {
411
+ components: defaultMdxComponents
412
+ });
413
+ }
414
+
415
+ function heading(depth, child, ctx) {
416
+ const id = ctx.slugger.slug(child);
417
+ return /*#__PURE__*/ jsx(Heading, {
418
+ id: id,
419
+ as: `h${depth.toString()}`,
420
+ children: child.trim()
421
+ }, id);
422
+ }
423
+
424
+ const keys = {
425
+ example: 'Example',
426
+ default: 'Default',
427
+ minimum: 'Minimum',
428
+ maximum: 'Maximum',
429
+ minLength: 'Minimum length',
430
+ maxLength: 'Maximum length',
431
+ pattern: 'Pattern',
432
+ format: 'Format'
433
+ };
434
+ function isObject(schema) {
435
+ return schema.type === 'object' || schema.properties !== undefined || schema.additionalProperties !== undefined;
436
+ }
437
+ function Schema({ name, schema, ctx }) {
438
+ if (schema.readOnly === true && !ctx.readOnly || schema.writeOnly === true && !ctx.writeOnly) return null;
439
+ const parseObject = ctx.parseObject ?? true;
440
+ const stack = ctx.stack ?? [];
441
+ const { renderer } = ctx.render;
442
+ const child = [];
443
+ function field(key, value) {
444
+ child.push(/*#__PURE__*/ jsxs("span", {
445
+ children: [
446
+ key,
447
+ ": ",
448
+ /*#__PURE__*/ jsx("code", {
449
+ children: value
450
+ })
451
+ ]
452
+ }, key));
453
+ }
454
+ // object type
455
+ if (isObject(schema) && parseObject) {
456
+ const { additionalProperties, properties } = schema;
457
+ if (additionalProperties === true) {
458
+ child.push(/*#__PURE__*/ jsx(renderer.Property, {
459
+ name: "[key: string]",
460
+ type: "any"
461
+ }, "additionalProperties"));
462
+ } else if (additionalProperties) {
463
+ child.push(/*#__PURE__*/ jsx(Schema, {
464
+ name: "[key: string]",
465
+ schema: noRef(additionalProperties),
466
+ ctx: {
467
+ ...ctx,
468
+ required: false,
469
+ parseObject: false
470
+ }
471
+ }, "additionalProperties"));
472
+ }
473
+ if (properties) {
474
+ const rendered = Object.entries(properties).map(([key, value])=>{
475
+ return /*#__PURE__*/ jsx(Schema, {
476
+ name: key,
477
+ schema: noRef(value),
478
+ ctx: {
479
+ ...ctx,
480
+ required: schema.required?.includes(key) ?? false,
481
+ parseObject: false
482
+ }
483
+ }, key);
484
+ });
485
+ child.push(...rendered);
486
+ }
487
+ return child;
488
+ }
489
+ if (schema.description) child.push(/*#__PURE__*/ jsx(Markdown, {
490
+ text: schema.description
491
+ }, "description"));
492
+ for (const [key, value] of Object.entries(keys)){
493
+ if (key in schema) {
494
+ field(value, JSON.stringify(schema[key]));
495
+ }
496
+ }
497
+ // enum types
498
+ if (schema.enum) {
499
+ field('Value in', schema.enum.map((value)=>JSON.stringify(value)).join(' | '));
500
+ }
501
+ if (isObject(schema) && !parseObject) {
502
+ child.push(/*#__PURE__*/ jsx(renderer.ObjectCollapsible, {
503
+ name: "Attributes",
504
+ children: /*#__PURE__*/ jsx(Schema, {
505
+ name: name,
506
+ schema: schema,
507
+ ctx: {
508
+ ...ctx,
509
+ parseObject: true,
510
+ required: false
511
+ }
512
+ })
513
+ }, "attributes"));
514
+ } else if (schema.allOf) {
515
+ child.push(/*#__PURE__*/ jsx(renderer.ObjectCollapsible, {
516
+ name: name,
517
+ children: /*#__PURE__*/ jsx(Schema, {
518
+ name: name,
519
+ schema: combineSchema(schema.allOf.map(noRef)),
520
+ ctx: {
521
+ ...ctx,
522
+ parseObject: true,
523
+ required: false
524
+ }
525
+ })
526
+ }, "attributes"));
527
+ } else {
528
+ const mentionedObjectTypes = [
529
+ ...schema.anyOf ?? schema.oneOf ?? [],
530
+ ...schema.not ? [
531
+ schema.not
532
+ ] : [],
533
+ ...schema.type === 'array' ? [
534
+ schema.items
535
+ ] : []
536
+ ].map(noRef).filter((s)=>isComplexType(s) && !stack.includes(s));
537
+ const renderedMentionedTypes = mentionedObjectTypes.map((s, idx)=>{
538
+ return /*#__PURE__*/ jsx(renderer.ObjectCollapsible, {
539
+ name: s.title ?? `Object ${(idx + 1).toString()}`,
540
+ children: /*#__PURE__*/ jsx(Schema, {
541
+ name: "element",
542
+ schema: noRef(s),
543
+ ctx: {
544
+ ...ctx,
545
+ stack: [
546
+ schema,
547
+ ...stack
548
+ ],
549
+ parseObject: true,
550
+ required: false
551
+ }
552
+ })
553
+ }, `mentioned:${idx.toString()}`);
554
+ });
555
+ child.push(...renderedMentionedTypes);
556
+ }
557
+ return /*#__PURE__*/ jsx(renderer.Property, {
558
+ name: name,
559
+ type: getSchemaType(schema, ctx),
560
+ deprecated: schema.deprecated,
561
+ children: child
562
+ });
563
+ }
564
+ /**
565
+ * Combine multiple object schemas into one
566
+ */ function combineSchema(schema) {
567
+ const result = {
568
+ type: 'object'
569
+ };
570
+ function add(s) {
571
+ var _result, _result1;
572
+ (_result = result).properties ?? (_result.properties = {});
573
+ if (s.properties) {
574
+ Object.assign(result.properties, s.properties);
575
+ }
576
+ (_result1 = result).additionalProperties ?? (_result1.additionalProperties = {});
577
+ if (s.additionalProperties === true) {
578
+ result.additionalProperties = true;
579
+ } else if (s.additionalProperties && typeof result.additionalProperties !== 'boolean') {
580
+ Object.assign(result.additionalProperties, s.additionalProperties);
581
+ }
582
+ if (s.allOf) {
583
+ add(combineSchema(s.allOf.map(noRef)));
584
+ }
585
+ }
586
+ schema.forEach(add);
587
+ return result;
588
+ }
589
+ /**
590
+ * Check if the schema needs another collapsible to explain
591
+ */ function isComplexType(schema) {
592
+ if (schema.anyOf ?? schema.oneOf ?? schema.allOf) return true;
593
+ return isObject(schema) || schema.type === 'array';
594
+ }
595
+ function getSchemaType(schema, ctx) {
596
+ if (schema.nullable) {
597
+ const type = getSchemaType({
598
+ ...schema,
599
+ nullable: false
600
+ }, ctx);
601
+ // null if schema only contains `nullable`
602
+ return type === 'unknown' ? 'null' : `${type} | null`;
603
+ }
604
+ if (schema.title) return schema.title;
605
+ if (schema.type === 'array') return `array<${getSchemaType(noRef(schema.items), ctx)}>`;
606
+ if (schema.oneOf) return schema.oneOf.map((one)=>getSchemaType(noRef(one), ctx)).join(' | ');
607
+ if (schema.allOf) return schema.allOf.map((one)=>getSchemaType(noRef(one), ctx)).join(' & ');
608
+ if (schema.not) return `not ${getSchemaType(noRef(schema.not), ctx)}`;
609
+ if (schema.anyOf) {
610
+ return `Any properties in ${schema.anyOf.map((one)=>getSchemaType(noRef(one), ctx)).join(', ')}`;
611
+ }
612
+ if (schema.type === 'string' && schema.format === 'binary' && ctx.allowFile) return 'file';
613
+ if (schema.type) return schema.type;
614
+ if (isObject(schema)) return 'object';
615
+ return 'unknown';
616
+ }
617
+
618
+ function Operation({ path, method, ctx, hasHead }) {
619
+ let level = 2;
620
+ const body = noRef(method.requestBody);
621
+ const security = method.security ?? ctx.document.security;
622
+ const info = [];
623
+ if (hasHead) {
624
+ info.push(heading(level, method.summary ?? (method.operationId ? idToTitle(method.operationId) : path), ctx));
625
+ level++;
626
+ if (method.description) {
627
+ info.push(/*#__PURE__*/ jsx(Markdown, {
628
+ text: method.description
629
+ }, "description"));
630
+ }
631
+ }
632
+ info.push(/*#__PURE__*/ jsx(Playground, {
633
+ path: path,
634
+ method: method,
635
+ ctx: ctx
636
+ }, "playground"));
637
+ if (security) {
638
+ info.push(heading(level, 'Authorization', ctx));
639
+ info.push(/*#__PURE__*/ jsx(AuthSection, {
640
+ requirements: security,
641
+ ctx: ctx
642
+ }, "auth"));
643
+ }
644
+ if (body) {
645
+ const type = getPreferredType(body.content);
646
+ if (!type) throw new Error(`No supported media type for body content: ${path}`);
647
+ info.push(/*#__PURE__*/ jsxs(Fragment, {
648
+ children: [
649
+ heading(level, 'Request Body', ctx),
650
+ /*#__PURE__*/ jsxs("div", {
651
+ className: "mb-8 flex flex-row items-center justify-between gap-2",
652
+ children: [
653
+ /*#__PURE__*/ jsx("code", {
654
+ children: type
655
+ }),
656
+ /*#__PURE__*/ jsx("span", {
657
+ children: body.required ? 'Required' : 'Optional'
658
+ })
659
+ ]
660
+ }),
661
+ body.description ? /*#__PURE__*/ jsx(Markdown, {
662
+ text: body.description
663
+ }) : null,
664
+ /*#__PURE__*/ jsx(Schema, {
665
+ name: "body",
666
+ schema: noRef(body.content[type].schema ?? {}),
667
+ ctx: {
668
+ readOnly: method.method === 'GET',
669
+ writeOnly: method.method !== 'GET',
670
+ required: body.required ?? false,
671
+ render: ctx,
672
+ allowFile: type === 'multipart/form-data'
673
+ }
674
+ })
675
+ ]
676
+ }, "body"));
677
+ }
678
+ const parameterGroups = new Map();
679
+ const endpoint = generateSample(path, method, ctx.baseUrl);
680
+ for (const param of method.parameters){
681
+ const pInfo = endpoint.parameters.find((item)=>item.name === param.name && item.in === param.in);
682
+ if (!pInfo) continue;
683
+ const schema = pInfo.schema;
684
+ const groupName = {
685
+ path: 'Path Parameters',
686
+ query: 'Query Parameters',
687
+ header: 'Header Parameters',
688
+ cookie: 'Cookie Parameters'
689
+ }[param.in] ?? 'Other Parameters';
690
+ const group = parameterGroups.get(groupName) ?? [];
691
+ group.push(/*#__PURE__*/ jsx(Schema, {
692
+ name: param.name,
693
+ schema: {
694
+ ...schema,
695
+ description: param.description ?? schema.description,
696
+ deprecated: (param.deprecated ?? false) || (schema.deprecated ?? false)
697
+ },
698
+ ctx: {
699
+ parseObject: false,
700
+ readOnly: method.method === 'GET',
701
+ writeOnly: method.method !== 'GET',
702
+ required: param.required ?? false,
703
+ render: ctx
704
+ }
705
+ }, param.name));
706
+ parameterGroups.set(groupName, group);
707
+ }
708
+ for (const [group, parameters] of Array.from(parameterGroups.entries())){
709
+ info.push(heading(level, group, ctx), ...parameters);
710
+ }
711
+ return /*#__PURE__*/ jsxs(ctx.renderer.API, {
712
+ children: [
713
+ /*#__PURE__*/ jsx(ctx.renderer.APIInfo, {
714
+ method: method.method,
715
+ route: path,
716
+ children: info
717
+ }),
718
+ /*#__PURE__*/ jsx(APIExample, {
719
+ method: method,
720
+ endpoint: endpoint,
721
+ ctx: ctx
722
+ })
723
+ ]
724
+ });
725
+ }
726
+ async function APIExample({ method, endpoint, ctx }) {
727
+ const renderer = ctx.renderer;
728
+ const children = [];
729
+ const samples = dedupe([
730
+ {
731
+ label: 'cURL',
732
+ source: getSampleRequest$2(endpoint),
733
+ lang: 'bash'
734
+ },
735
+ {
736
+ label: 'JavaScript',
737
+ source: getSampleRequest$1(endpoint),
738
+ lang: 'js'
739
+ },
740
+ {
741
+ label: 'Go',
742
+ source: getSampleRequest(endpoint),
743
+ lang: 'go'
744
+ },
745
+ ...ctx.generateCodeSamples ? await ctx.generateCodeSamples(endpoint) : [],
746
+ ...method['x-codeSamples'] ?? []
747
+ ]);
748
+ children.push(/*#__PURE__*/ jsx(renderer.Requests, {
749
+ items: samples.map((s)=>s.label),
750
+ children: samples.map((s)=>/*#__PURE__*/ jsx(renderer.Request, {
751
+ name: s.label,
752
+ code: s.source,
753
+ language: s.lang
754
+ }, s.label))
755
+ }, "requests"));
756
+ children.push(/*#__PURE__*/ jsx(ResponseTabs, {
757
+ operation: method,
758
+ ctx: ctx,
759
+ endpoint: endpoint
760
+ }, "responses"));
761
+ return /*#__PURE__*/ jsx(renderer.APIExample, {
762
+ children: children
763
+ });
764
+ }
765
+ /**
766
+ * Remove duplicated labels
767
+ */ function dedupe(samples) {
768
+ const set = new Set();
769
+ const out = [];
770
+ for(let i = samples.length - 1; i >= 0; i--){
771
+ if (set.has(samples[i].label)) continue;
772
+ set.add(samples[i].label);
773
+ out.unshift(samples[i]);
774
+ }
775
+ return out;
776
+ }
777
+ function AuthSection({ ctx: { document, renderer }, requirements }) {
778
+ let id = 0;
779
+ const info = [];
780
+ for (const requirement of requirements){
781
+ if (info.length > 0) info.push(`---`);
782
+ for (const schema of getScheme(requirement, document)){
783
+ if (schema.type === 'http') {
784
+ info.push(/*#__PURE__*/ jsxs(renderer.Property, {
785
+ name: "Authorization",
786
+ type: {
787
+ basic: 'Basic <token>',
788
+ bearer: 'Bearer <token>'
789
+ }[schema.scheme] ?? '<token>',
790
+ required: true,
791
+ children: [
792
+ schema.description ? /*#__PURE__*/ jsx(Markdown, {
793
+ text: schema.description
794
+ }) : null,
795
+ /*#__PURE__*/ jsxs("p", {
796
+ children: [
797
+ "In: ",
798
+ /*#__PURE__*/ jsx("code", {
799
+ children: "header"
800
+ })
801
+ ]
802
+ })
803
+ ]
804
+ }, id++));
805
+ }
806
+ if (schema.type === 'oauth2') {
807
+ info.push(/*#__PURE__*/ jsxs(renderer.Property, {
808
+ name: "Authorization",
809
+ type: "Bearer <token>",
810
+ required: true,
811
+ children: [
812
+ schema.description ? /*#__PURE__*/ jsx(Markdown, {
813
+ text: schema.description
814
+ }) : null,
815
+ /*#__PURE__*/ jsxs("p", {
816
+ children: [
817
+ "In: ",
818
+ /*#__PURE__*/ jsx("code", {
819
+ children: "header"
820
+ })
821
+ ]
822
+ }),
823
+ /*#__PURE__*/ jsxs("p", {
824
+ children: [
825
+ "Scope:",
826
+ ' ',
827
+ /*#__PURE__*/ jsx("code", {
828
+ children: schema.scopes.length > 0 ? schema.scopes.join(', ') : 'none'
829
+ })
830
+ ]
831
+ })
832
+ ]
833
+ }, id++));
834
+ }
835
+ if (schema.type === 'apiKey') {
836
+ info.push(/*#__PURE__*/ jsxs(renderer.Property, {
837
+ name: schema.name,
838
+ type: "<token>",
839
+ children: [
840
+ schema.description ? /*#__PURE__*/ jsx(Markdown, {
841
+ text: schema.description
842
+ }) : null,
843
+ /*#__PURE__*/ jsxs("p", {
844
+ children: [
845
+ "In: ",
846
+ /*#__PURE__*/ jsx("code", {
847
+ children: schema.in
848
+ })
849
+ ]
850
+ })
851
+ ]
852
+ }, id++));
853
+ }
854
+ if (schema.type === 'openIdConnect') {
855
+ info.push(/*#__PURE__*/ jsx(renderer.Property, {
856
+ name: "OpenID Connect",
857
+ type: "<token>",
858
+ required: true,
859
+ children: schema.description ? /*#__PURE__*/ jsx(Markdown, {
860
+ text: schema.description
861
+ }) : null
862
+ }, id++));
863
+ }
864
+ }
865
+ }
866
+ return info;
867
+ }
868
+ async function ResponseTabs({ endpoint, operation, ctx: { renderer, generateTypeScriptSchema } }) {
869
+ const items = [];
870
+ const children = [];
871
+ for (const code of Object.keys(operation.responses)){
872
+ const types = [];
873
+ let description = noRef(operation.responses[code]).description;
874
+ if (!description && code in endpoint.responses) description = endpoint.responses[code].schema.description ?? '';
875
+ if (code in endpoint.responses) {
876
+ types.push({
877
+ lang: 'json',
878
+ label: 'Response',
879
+ code: JSON.stringify(endpoint.responses[code].sample, null, 2)
880
+ });
881
+ }
882
+ let ts;
883
+ if (generateTypeScriptSchema) {
884
+ ts = await generateTypeScriptSchema(endpoint, code);
885
+ } else if (generateTypeScriptSchema === undefined) {
886
+ ts = await getTypescriptSchema(endpoint, code);
887
+ }
888
+ if (ts) {
889
+ types.push({
890
+ code: ts,
891
+ lang: 'ts',
892
+ label: 'TypeScript'
893
+ });
894
+ }
895
+ items.push(code);
896
+ children.push(/*#__PURE__*/ jsxs(renderer.Response, {
897
+ value: code,
898
+ children: [
899
+ /*#__PURE__*/ jsx(Markdown, {
900
+ text: description
901
+ }),
902
+ types.length > 0 ? /*#__PURE__*/ jsx(renderer.ResponseTypes, {
903
+ children: types.map((type)=>/*#__PURE__*/ jsx(renderer.ResponseType, {
904
+ ...type
905
+ }, type.lang))
906
+ }) : null
907
+ ]
908
+ }, code));
909
+ }
910
+ if (items.length === 0) return null;
911
+ return /*#__PURE__*/ jsx(renderer.Responses, {
912
+ items: items,
913
+ children: children
914
+ });
915
+ }
916
+
917
+ function createMethod(method, operation) {
918
+ return {
919
+ ...operation,
920
+ parameters: operation.parameters ?? [],
921
+ method: method.toUpperCase()
922
+ };
923
+ }
924
+
925
+ const highlighter = await createHighlighter({
926
+ themes: Object.values(bundledThemes),
927
+ langs: Object.values(bundledLanguages)
928
+ });
929
+ function CodeBlock({ code, lang, ...props }) {
930
+ const html = useMemo(()=>{
931
+ return highlighter.codeToHtml(code, {
932
+ lang,
933
+ defaultColor: false,
934
+ themes: {
935
+ light: 'github-light',
936
+ dark: 'github-dark'
937
+ }
938
+ });
939
+ }, [
940
+ code,
941
+ lang
942
+ ]);
943
+ return /*#__PURE__*/ jsx(Base.CodeBlock, {
944
+ className: "my-0",
945
+ children: /*#__PURE__*/ jsx(Base.Pre, {
946
+ ...props,
947
+ dangerouslySetInnerHTML: {
948
+ __html: html
949
+ }
950
+ })
951
+ });
952
+ }
953
+
954
+ const defaultRenderer = {
955
+ Root,
956
+ API,
957
+ APIInfo,
958
+ APIExample: APIExample$1,
959
+ Responses: Tabs,
960
+ Response: Tab,
961
+ ResponseTypes: (props)=>/*#__PURE__*/ jsx(Accordions, {
962
+ type: "single",
963
+ className: "!-m-4 border-none pt-2",
964
+ defaultValue: "Response",
965
+ children: props.children
966
+ }),
967
+ ResponseType: (props)=>/*#__PURE__*/ jsx(Accordion, {
968
+ title: props.label,
969
+ children: /*#__PURE__*/ jsx(CodeBlock, {
970
+ code: props.code,
971
+ lang: props.lang
972
+ })
973
+ }),
974
+ Property,
975
+ ObjectCollapsible,
976
+ Requests: Tabs,
977
+ Request: (props)=>/*#__PURE__*/ jsx(Tab, {
978
+ value: props.name,
979
+ children: /*#__PURE__*/ jsx(CodeBlock, {
980
+ lang: props.language,
981
+ code: props.code
982
+ })
983
+ }),
984
+ APIPlayground
985
+ };
986
+
987
+ function APIPage(props) {
988
+ const { operations, document, hasHead = true } = props;
989
+ const ctx = getContext(document, props);
990
+ return /*#__PURE__*/ jsx(ctx.renderer.Root, {
991
+ baseUrl: ctx.baseUrl,
992
+ children: operations.map((item)=>{
993
+ const operation = document.paths[item.path]?.[item.method];
994
+ if (!operation) return null;
995
+ const method = createMethod(item.method, operation);
996
+ return /*#__PURE__*/ jsx(Operation, {
997
+ method: method,
998
+ path: item.path,
999
+ ctx: ctx,
1000
+ hasHead: hasHead
1001
+ }, `${item.path}:${item.method}`);
1002
+ })
1003
+ });
1004
+ }
1005
+ function getContext(document, options) {
1006
+ return {
1007
+ document,
1008
+ renderer: {
1009
+ ...defaultRenderer,
1010
+ ...options.renderer
1011
+ },
1012
+ generateTypeScriptSchema: options.generateTypeScriptSchema,
1013
+ generateCodeSamples: options.generateCodeSamples,
1014
+ baseUrl: document.servers?.[0].url ?? 'https://example.com',
1015
+ slugger: new Slugger()
1016
+ };
1017
+ }
1018
+
1019
+ function createOpenAPI(options) {
1020
+ const document = Parser.dereference(options.documentOrPath);
1021
+ return {
1022
+ APIPage: async (props)=>{
1023
+ return /*#__PURE__*/ jsx(APIPage, {
1024
+ document: await document,
1025
+ ...props
1026
+ });
1027
+ }
1028
+ };
1029
+ }
1030
+
1031
+ cva('rounded border px-1.5 py-1 text-xs font-medium leading-[12px]', {
1032
+ variants: {
1033
+ color: {
1034
+ green: 'border-green-400/50 bg-green-400/20 text-green-600 dark:text-green-400',
1035
+ yellow: 'border-yellow-400/50 bg-yellow-400/20 text-yellow-600 dark:text-yellow-400',
1036
+ red: 'border-red-400/50 bg-red-400/20 text-red-600 dark:text-red-400',
1037
+ blue: 'border-blue-400/50 bg-blue-400/20 text-blue-600 dark:text-blue-400',
1038
+ orange: 'border-orange-400/50 bg-orange-400/20 text-orange-600 dark:text-orange-400'
1039
+ }
1040
+ }
1041
+ });
1042
+ function getBadgeColor(method) {
1043
+ switch(method){
1044
+ case 'PUT':
1045
+ return 'yellow';
1046
+ case 'PATCH':
1047
+ return 'orange';
1048
+ case 'POST':
1049
+ return 'blue';
1050
+ case 'DELETE':
1051
+ return 'red';
1052
+ default:
1053
+ return 'green';
1054
+ }
1055
+ }
1056
+
1057
+ /**
1058
+ * Source API Integration
1059
+ *
1060
+ * Add this to page tree builder options
1061
+ */ const attachFile = (node, file)=>{
1062
+ if (!file) return node;
1063
+ const data = file.data.data;
1064
+ if ('method' in data && typeof data.method === 'string') {
1065
+ const color = getBadgeColor(data.method);
1066
+ node.name = /*#__PURE__*/ jsxs(Fragment$1, {
1067
+ children: [
1068
+ node.name,
1069
+ ' ',
1070
+ /*#__PURE__*/ jsx("span", {
1071
+ className: badgeVariants({
1072
+ className: 'ms-auto text-nowrap',
1073
+ color
1074
+ }),
1075
+ children: data.method
1076
+ })
1077
+ ]
1078
+ });
1079
+ }
1080
+ return node;
1081
+ };
1082
+ const badgeVariants = cva('rounded-full border px-1.5 text-xs font-medium', {
1083
+ variants: {
1084
+ color: {
1085
+ green: 'bg-green-400/20 text-green-600 dark:text-green-400',
1086
+ yellow: 'bg-yellow-400/20 text-yellow-600 dark:text-yellow-400',
1087
+ red: 'bg-red-400/20 text-red-600 dark:text-red-400',
1088
+ blue: 'bg-blue-400/20 text-blue-600 dark:text-blue-400',
1089
+ orange: 'bg-orange-400/20 text-orange-600 dark:text-orange-400'
1090
+ }
1091
+ }
1092
+ });
1093
+
1094
+ export { attachFile, createOpenAPI };