docusaurus-plugin-openapi-docs 1.0.6 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +1 -2
  2. package/lib/markdown/createSchemaDetails.js +325 -132
  3. package/lib/markdown/index.js +1 -0
  4. package/lib/markdown/schema.js +25 -9
  5. package/lib/markdown/utils.d.ts +1 -1
  6. package/lib/markdown/utils.js +4 -1
  7. package/lib/openapi/openapi.d.ts +3 -3
  8. package/lib/openapi/openapi.js +30 -26
  9. package/lib/openapi/types.d.ts +2 -1
  10. package/lib/openapi/utils/loadAndResolveSpec.d.ts +2 -0
  11. package/lib/openapi/utils/{loadAndBundleSpec.js → loadAndResolveSpec.js} +61 -28
  12. package/lib/openapi/utils/services/OpenAPIParser.d.ts +52 -0
  13. package/lib/openapi/utils/services/OpenAPIParser.js +342 -0
  14. package/lib/openapi/utils/services/RedocNormalizedOptions.d.ts +100 -0
  15. package/lib/openapi/utils/services/RedocNormalizedOptions.js +170 -0
  16. package/lib/openapi/utils/types/index.d.ts +2 -0
  17. package/lib/openapi/utils/types/index.js +23 -0
  18. package/lib/openapi/utils/types/open-api.d.ts +305 -0
  19. package/lib/openapi/utils/types/open-api.js +8 -0
  20. package/lib/openapi/utils/utils/JsonPointer.d.ts +51 -0
  21. package/lib/openapi/utils/utils/JsonPointer.js +95 -0
  22. package/lib/openapi/utils/utils/helpers.d.ts +43 -0
  23. package/lib/openapi/utils/utils/helpers.js +230 -0
  24. package/lib/openapi/utils/utils/index.d.ts +3 -0
  25. package/lib/openapi/utils/utils/index.js +25 -0
  26. package/lib/openapi/utils/utils/openapi.d.ts +40 -0
  27. package/lib/openapi/utils/utils/openapi.js +605 -0
  28. package/lib/sidebars/index.js +5 -3
  29. package/package.json +15 -11
  30. package/src/markdown/createSchemaDetails.ts +405 -159
  31. package/src/markdown/index.ts +1 -0
  32. package/src/markdown/schema.ts +28 -8
  33. package/src/markdown/utils.ts +5 -2
  34. package/src/openapi/openapi.ts +42 -38
  35. package/src/openapi/types.ts +2 -1
  36. package/src/openapi/utils/loadAndResolveSpec.ts +123 -0
  37. package/src/openapi/utils/services/OpenAPIParser.ts +433 -0
  38. package/src/openapi/utils/services/RedocNormalizedOptions.ts +330 -0
  39. package/src/openapi/utils/types/index.ts +10 -0
  40. package/src/openapi/utils/types/open-api.ts +303 -0
  41. package/src/openapi/utils/utils/JsonPointer.ts +99 -0
  42. package/src/openapi/utils/utils/helpers.ts +239 -0
  43. package/src/openapi/utils/utils/index.ts +11 -0
  44. package/src/openapi/utils/utils/openapi.ts +771 -0
  45. package/src/sidebars/index.ts +7 -4
  46. package/lib/openapi/utils/loadAndBundleSpec.d.ts +0 -3
  47. package/src/openapi/utils/loadAndBundleSpec.ts +0 -93
@@ -0,0 +1,771 @@
1
+ /* ============================================================================
2
+ * Copyright (c) Palo Alto Networks
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ * ========================================================================== */
7
+
8
+ // @ts-nocheck
9
+
10
+ import { dirname } from "path";
11
+
12
+ import { OpenAPIParser } from "../services/OpenAPIParser";
13
+ import {
14
+ OpenAPIEncoding,
15
+ OpenAPIMediaType,
16
+ OpenAPIParameter,
17
+ OpenAPIParameterStyle,
18
+ OpenAPIRequestBody,
19
+ OpenAPIResponse,
20
+ OpenAPISchema,
21
+ OpenAPIServer,
22
+ Referenced,
23
+ } from "../types";
24
+ import {
25
+ isNumeric,
26
+ removeQueryString,
27
+ resolveUrl,
28
+ isArray,
29
+ isBoolean,
30
+ } from "./helpers";
31
+
32
+ function isWildcardStatusCode(
33
+ statusCode: string | number
34
+ ): statusCode is string {
35
+ return typeof statusCode === "string" && /\dxx/i.test(statusCode);
36
+ }
37
+
38
+ export function isStatusCode(statusCode: string) {
39
+ return (
40
+ statusCode === "default" ||
41
+ isNumeric(statusCode) ||
42
+ isWildcardStatusCode(statusCode)
43
+ );
44
+ }
45
+
46
+ export function getStatusCodeType(
47
+ statusCode: string | number,
48
+ defaultAsError = false
49
+ ): string {
50
+ if (statusCode === "default") {
51
+ return defaultAsError ? "error" : "success";
52
+ }
53
+
54
+ let code =
55
+ typeof statusCode === "string" ? parseInt(statusCode, 10) : statusCode;
56
+ if (isWildcardStatusCode(statusCode)) {
57
+ code *= 100; // parseInt('2xx') parses to 2
58
+ }
59
+
60
+ if (code < 100 || code > 599) {
61
+ throw new Error("invalid HTTP code");
62
+ }
63
+ let res = "success";
64
+ if (code >= 300 && code < 400) {
65
+ res = "redirect";
66
+ } else if (code >= 400) {
67
+ res = "error";
68
+ } else if (code < 200) {
69
+ res = "info";
70
+ }
71
+ return res;
72
+ }
73
+
74
+ const operationNames = {
75
+ get: true,
76
+ post: true,
77
+ put: true,
78
+ head: true,
79
+ patch: true,
80
+ delete: true,
81
+ options: true,
82
+ $ref: true,
83
+ };
84
+
85
+ export function isOperationName(key: string): boolean {
86
+ return key in operationNames;
87
+ }
88
+
89
+ export function getOperationSummary(operation: any): string {
90
+ return (
91
+ operation.summary ||
92
+ operation.operationId ||
93
+ (operation.description && operation.description.substring(0, 50)) ||
94
+ operation.pathName ||
95
+ "<no summary>"
96
+ );
97
+ }
98
+
99
+ const schemaKeywordTypes = {
100
+ multipleOf: "number",
101
+ maximum: "number",
102
+ exclusiveMaximum: "number",
103
+ minimum: "number",
104
+ exclusiveMinimum: "number",
105
+
106
+ maxLength: "string",
107
+ minLength: "string",
108
+ pattern: "string",
109
+ contentEncoding: "string",
110
+ contentMediaType: "string",
111
+
112
+ items: "array",
113
+ maxItems: "array",
114
+ minItems: "array",
115
+ uniqueItems: "array",
116
+
117
+ maxProperties: "object",
118
+ minProperties: "object",
119
+ required: "object",
120
+ additionalProperties: "object",
121
+ unevaluatedProperties: "object",
122
+ properties: "object",
123
+ patternProperties: "object",
124
+ };
125
+
126
+ export function detectType(schema: OpenAPISchema): string {
127
+ if (schema.type !== undefined && !isArray(schema.type)) {
128
+ return schema.type;
129
+ }
130
+ const keywords = Object.keys(schemaKeywordTypes);
131
+ for (const keyword of keywords) {
132
+ const type = schemaKeywordTypes[keyword];
133
+ if (schema[keyword] !== undefined) {
134
+ return type;
135
+ }
136
+ }
137
+
138
+ return "any";
139
+ }
140
+
141
+ export function isPrimitiveType(
142
+ schema: OpenAPISchema,
143
+ type: string | string[] | undefined = schema.type
144
+ ) {
145
+ if (schema.oneOf !== undefined || schema.anyOf !== undefined) {
146
+ return false;
147
+ }
148
+
149
+ if ((schema.if && schema.then) || (schema.if && schema.else)) {
150
+ return false;
151
+ }
152
+
153
+ let isPrimitive = true;
154
+ const isArrayType = isArray(type);
155
+
156
+ if (type === "object" || (isArrayType && type?.includes("object"))) {
157
+ isPrimitive =
158
+ schema.properties !== undefined
159
+ ? Object.keys(schema.properties).length === 0
160
+ : schema.additionalProperties === undefined &&
161
+ schema.unevaluatedProperties === undefined;
162
+ }
163
+
164
+ if (isArray(schema.items) || isArray(schema.prefixItems)) {
165
+ return false;
166
+ }
167
+
168
+ if (
169
+ schema.items !== undefined &&
170
+ !isBoolean(schema.items) &&
171
+ (type === "array" || (isArrayType && type?.includes("array")))
172
+ ) {
173
+ isPrimitive = isPrimitiveType(schema.items, schema.items.type);
174
+ }
175
+
176
+ return isPrimitive;
177
+ }
178
+
179
+ export function isJsonLike(contentType: string): boolean {
180
+ return contentType.search(/json/i) !== -1;
181
+ }
182
+
183
+ export function isFormUrlEncoded(contentType: string): boolean {
184
+ return contentType === "application/x-www-form-urlencoded";
185
+ }
186
+
187
+ function delimitedEncodeField(
188
+ fieldVal: any,
189
+ fieldName: string,
190
+ delimiter: string
191
+ ): string {
192
+ if (isArray(fieldVal)) {
193
+ return fieldVal.map((v) => v.toString()).join(delimiter);
194
+ } else if (typeof fieldVal === "object") {
195
+ return Object.keys(fieldVal)
196
+ .map((k) => `${k}${delimiter}${fieldVal[k]}`)
197
+ .join(delimiter);
198
+ } else {
199
+ return fieldName + "=" + fieldVal.toString();
200
+ }
201
+ }
202
+
203
+ function deepObjectEncodeField(fieldVal: any, fieldName: string): string {
204
+ if (isArray(fieldVal)) {
205
+ console.warn(
206
+ "deepObject style cannot be used with array value:" + fieldVal.toString()
207
+ );
208
+ return "";
209
+ } else if (typeof fieldVal === "object") {
210
+ return Object.keys(fieldVal)
211
+ .map((k) => `${fieldName}[${k}]=${fieldVal[k]}`)
212
+ .join("&");
213
+ } else {
214
+ console.warn(
215
+ "deepObject style cannot be used with non-object value:" +
216
+ fieldVal.toString()
217
+ );
218
+ return "";
219
+ }
220
+ }
221
+
222
+ function serializeFormValue(name: string, explode: boolean, value: any) {
223
+ // Use RFC6570 safe name ([a-zA-Z0-9_]) and replace with our name later
224
+ // e.g. URI.template doesn't parse names with hyphen (-) which are valid query param names
225
+ const safeName = "__redoc_param_name__";
226
+ const suffix = explode ? "*" : "";
227
+ const template = `{?${safeName}${suffix}}`;
228
+ return template
229
+ .expand({ [safeName]: value })
230
+ .substring(1)
231
+ .replace(/__redoc_param_name__/g, name);
232
+ }
233
+
234
+ /*
235
+ * Should be used only for url-form-encoded body payloads
236
+ * To be used for parameters should be extended with other style values
237
+ */
238
+ export function urlFormEncodePayload(
239
+ payload: object,
240
+ encoding: { [field: string]: OpenAPIEncoding } = {}
241
+ ) {
242
+ if (isArray(payload)) {
243
+ throw new Error("Payload must have fields: " + payload.toString());
244
+ } else {
245
+ return Object.keys(payload)
246
+ .map((fieldName) => {
247
+ const fieldVal = payload[fieldName];
248
+ const { style = "form", explode = true } = encoding[fieldName] || {};
249
+ switch (style) {
250
+ case "form":
251
+ return serializeFormValue(fieldName, explode, fieldVal);
252
+ case "spaceDelimited":
253
+ return delimitedEncodeField(fieldVal, fieldName, "%20");
254
+ case "pipeDelimited":
255
+ return delimitedEncodeField(fieldVal, fieldName, "|");
256
+ case "deepObject":
257
+ return deepObjectEncodeField(fieldVal, fieldName);
258
+ default:
259
+ // TODO implement rest of styles for path parameters
260
+ console.warn("Incorrect or unsupported encoding style: " + style);
261
+ return "";
262
+ }
263
+ })
264
+ .join("&");
265
+ }
266
+ }
267
+
268
+ function serializePathParameter(
269
+ name: string,
270
+ style: OpenAPIParameterStyle,
271
+ explode: boolean,
272
+ value: any
273
+ ): string {
274
+ const suffix = explode ? "*" : "";
275
+ let prefix = "";
276
+
277
+ if (style === "label") {
278
+ prefix = ".";
279
+ } else if (style === "matrix") {
280
+ prefix = ";";
281
+ }
282
+
283
+ // Use RFC6570 safe name ([a-zA-Z0-9_]) and replace with our name later
284
+ // e.g. URI.template doesn't parse names with hyphen (-) which are valid query param names
285
+ const safeName = "__redoc_param_name__";
286
+ const template = `{${prefix}${safeName}${suffix}}`;
287
+
288
+ return template
289
+ .expand({ [safeName]: value })
290
+ .replace(/__redoc_param_name__/g, name);
291
+ }
292
+
293
+ function serializeQueryParameter(
294
+ name: string,
295
+ style: OpenAPIParameterStyle,
296
+ explode: boolean,
297
+ value: any
298
+ ): string {
299
+ switch (style) {
300
+ case "form":
301
+ return serializeFormValue(name, explode, value);
302
+ case "spaceDelimited":
303
+ if (!isArray(value)) {
304
+ console.warn("The style spaceDelimited is only applicable to arrays");
305
+ return "";
306
+ }
307
+ if (explode) {
308
+ return serializeFormValue(name, explode, value);
309
+ }
310
+
311
+ return `${name}=${value.join("%20")}`;
312
+ case "pipeDelimited":
313
+ if (!isArray(value)) {
314
+ console.warn("The style pipeDelimited is only applicable to arrays");
315
+ return "";
316
+ }
317
+ if (explode) {
318
+ return serializeFormValue(name, explode, value);
319
+ }
320
+
321
+ return `${name}=${value.join("|")}`;
322
+ case "deepObject":
323
+ if (!explode || isArray(value) || typeof value !== "object") {
324
+ console.warn(
325
+ "The style deepObject is only applicable for objects with explode=true"
326
+ );
327
+ return "";
328
+ }
329
+
330
+ return deepObjectEncodeField(value, name);
331
+ default:
332
+ console.warn("Unexpected style for query: " + style);
333
+ return "";
334
+ }
335
+ }
336
+
337
+ function serializeHeaderParameter(
338
+ style: OpenAPIParameterStyle,
339
+ explode: boolean,
340
+ value: any
341
+ ): string {
342
+ switch (style) {
343
+ case "simple":
344
+ const suffix = explode ? "*" : "";
345
+
346
+ // name is not important here, so use RFC6570 safe name ([a-zA-Z0-9_])
347
+ const name = "__redoc_param_name__";
348
+ const template = `{${name}${suffix}}`;
349
+ return decodeURIComponent(template.expand({ [name]: value }));
350
+ default:
351
+ console.warn("Unexpected style for header: " + style);
352
+ return "";
353
+ }
354
+ }
355
+
356
+ function serializeCookieParameter(
357
+ name: string,
358
+ style: OpenAPIParameterStyle,
359
+ explode: boolean,
360
+ value: any
361
+ ): string {
362
+ switch (style) {
363
+ case "form":
364
+ return serializeFormValue(name, explode, value);
365
+ default:
366
+ console.warn("Unexpected style for cookie: " + style);
367
+ return "";
368
+ }
369
+ }
370
+
371
+ export function serializeParameterValueWithMime(
372
+ value: any,
373
+ mime: string
374
+ ): string {
375
+ if (isJsonLike(mime)) {
376
+ return JSON.stringify(value);
377
+ } else {
378
+ console.warn(`Parameter serialization as ${mime} is not supported`);
379
+ return "";
380
+ }
381
+ }
382
+
383
+ export function serializeParameterValue(
384
+ parameter: OpenAPIParameter & { serializationMime?: string },
385
+ value: any
386
+ ): string {
387
+ const { name, style, explode = false, serializationMime } = parameter;
388
+
389
+ if (serializationMime) {
390
+ switch (parameter.in) {
391
+ case "path":
392
+ case "header":
393
+ return serializeParameterValueWithMime(value, serializationMime);
394
+ case "cookie":
395
+ case "query":
396
+ return `${name}=${serializeParameterValueWithMime(
397
+ value,
398
+ serializationMime
399
+ )}`;
400
+ default:
401
+ console.warn("Unexpected parameter location: " + parameter.in);
402
+ return "";
403
+ }
404
+ }
405
+
406
+ if (!style) {
407
+ console.warn(`Missing style attribute or content for parameter ${name}`);
408
+ return "";
409
+ }
410
+
411
+ switch (parameter.in) {
412
+ case "path":
413
+ return serializePathParameter(name, style, explode, value);
414
+ case "query":
415
+ return serializeQueryParameter(name, style, explode, value);
416
+ case "header":
417
+ return serializeHeaderParameter(style, explode, value);
418
+ case "cookie":
419
+ return serializeCookieParameter(name, style, explode, value);
420
+ default:
421
+ console.warn("Unexpected parameter location: " + parameter.in);
422
+ return "";
423
+ }
424
+ }
425
+
426
+ export function getSerializedValue(field: any, example: any) {
427
+ if (field.in) {
428
+ // decode for better readability in examples: see https://github.com/Redocly/redoc/issues/1138
429
+ return decodeURIComponent(serializeParameterValue(field, example));
430
+ } else {
431
+ return example;
432
+ }
433
+ }
434
+
435
+ export function langFromMime(contentType: string): string {
436
+ if (contentType.search(/xml/i) !== -1) {
437
+ return "xml";
438
+ }
439
+ return "clike";
440
+ }
441
+
442
+ const DEFINITION_NAME_REGEX = /^#\/components\/(schemas|pathItems)\/([^/]+)$/;
443
+
444
+ export function isNamedDefinition(pointer?: string): boolean {
445
+ return DEFINITION_NAME_REGEX.test(pointer || "");
446
+ }
447
+
448
+ export function getDefinitionName(pointer?: string): string | undefined {
449
+ const [name] = pointer?.match(DEFINITION_NAME_REGEX)?.reverse() || [];
450
+ return name;
451
+ }
452
+
453
+ function humanizeMultipleOfConstraint(
454
+ multipleOf: number | undefined
455
+ ): string | undefined {
456
+ if (multipleOf === undefined) {
457
+ return;
458
+ }
459
+ const strigifiedMultipleOf = multipleOf.toString(10);
460
+ if (!/^0\.0*1$/.test(strigifiedMultipleOf)) {
461
+ return `multiple of ${strigifiedMultipleOf}`;
462
+ }
463
+ return `decimal places <= ${strigifiedMultipleOf.split(".")[1].length}`;
464
+ }
465
+
466
+ function humanizeRangeConstraint(
467
+ description: string,
468
+ min: number | undefined,
469
+ max: number | undefined
470
+ ): string | undefined {
471
+ let stringRange;
472
+ if (min !== undefined && max !== undefined) {
473
+ if (min === max) {
474
+ stringRange = `= ${min} ${description}`;
475
+ } else {
476
+ stringRange = `[ ${min} .. ${max} ] ${description}`;
477
+ }
478
+ } else if (max !== undefined) {
479
+ stringRange = `<= ${max} ${description}`;
480
+ } else if (min !== undefined) {
481
+ if (min === 1) {
482
+ stringRange = "non-empty";
483
+ } else {
484
+ stringRange = `>= ${min} ${description}`;
485
+ }
486
+ }
487
+
488
+ return stringRange;
489
+ }
490
+
491
+ export function humanizeNumberRange(schema: OpenAPISchema): string | undefined {
492
+ const minimum =
493
+ typeof schema.exclusiveMinimum === "number"
494
+ ? Math.min(schema.exclusiveMinimum, schema.minimum ?? Infinity)
495
+ : schema.minimum;
496
+ const maximum =
497
+ typeof schema.exclusiveMaximum === "number"
498
+ ? Math.max(schema.exclusiveMaximum, schema.maximum ?? -Infinity)
499
+ : schema.maximum;
500
+ const exclusiveMinimum =
501
+ typeof schema.exclusiveMinimum === "number" || schema.exclusiveMinimum;
502
+ const exclusiveMaximum =
503
+ typeof schema.exclusiveMaximum === "number" || schema.exclusiveMaximum;
504
+
505
+ if (minimum !== undefined && maximum !== undefined) {
506
+ return `${exclusiveMinimum ? "( " : "[ "}${minimum} .. ${maximum}${
507
+ exclusiveMaximum ? " )" : " ]"
508
+ }`;
509
+ } else if (maximum !== undefined) {
510
+ return `${exclusiveMaximum ? "< " : "<= "}${maximum}`;
511
+ } else if (minimum !== undefined) {
512
+ return `${exclusiveMinimum ? "> " : ">= "}${minimum}`;
513
+ }
514
+ }
515
+
516
+ export function humanizeConstraints(schema: OpenAPISchema): string[] {
517
+ const res: string[] = [];
518
+
519
+ const stringRange = humanizeRangeConstraint(
520
+ "characters",
521
+ schema.minLength,
522
+ schema.maxLength
523
+ );
524
+ if (stringRange !== undefined) {
525
+ res.push(stringRange);
526
+ }
527
+
528
+ const arrayRange = humanizeRangeConstraint(
529
+ "items",
530
+ schema.minItems,
531
+ schema.maxItems
532
+ );
533
+ if (arrayRange !== undefined) {
534
+ res.push(arrayRange);
535
+ }
536
+
537
+ const propertiesRange = humanizeRangeConstraint(
538
+ "properties",
539
+ schema.minProperties,
540
+ schema.maxProperties
541
+ );
542
+ if (propertiesRange !== undefined) {
543
+ res.push(propertiesRange);
544
+ }
545
+
546
+ const multipleOfConstraint = humanizeMultipleOfConstraint(schema.multipleOf);
547
+ if (multipleOfConstraint !== undefined) {
548
+ res.push(multipleOfConstraint);
549
+ }
550
+
551
+ const numberRange = humanizeNumberRange(schema);
552
+ if (numberRange !== undefined) {
553
+ res.push(numberRange);
554
+ }
555
+
556
+ if (schema.uniqueItems) {
557
+ res.push("unique");
558
+ }
559
+
560
+ return res;
561
+ }
562
+
563
+ export function sortByRequired(fields: any[], order: string[] = []) {
564
+ const unrequiredFields: any[] = [];
565
+ const orderedFields: any[] = [];
566
+ const unorderedFields: any[] = [];
567
+
568
+ fields.forEach((field) => {
569
+ if (field.required) {
570
+ order.includes(field.name)
571
+ ? orderedFields.push(field)
572
+ : unorderedFields.push(field);
573
+ } else {
574
+ unrequiredFields.push(field);
575
+ }
576
+ });
577
+
578
+ orderedFields.sort((a, b) => order.indexOf(a.name) - order.indexOf(b.name));
579
+
580
+ return [...orderedFields, ...unorderedFields, ...unrequiredFields];
581
+ }
582
+
583
+ export function sortByField(
584
+ fields: any[],
585
+ param: "name" | "description" | "kind"
586
+ ) {
587
+ return [...fields].sort((a, b) => {
588
+ return a[param].localeCompare(b[param]);
589
+ });
590
+ }
591
+
592
+ export function mergeParams(
593
+ parser: OpenAPIParser,
594
+ pathParams: Array<Referenced<OpenAPIParameter>> = [],
595
+ operationParams: Array<Referenced<OpenAPIParameter>> = []
596
+ ): Array<Referenced<OpenAPIParameter>> {
597
+ const operationParamNames = {};
598
+ operationParams.forEach((param) => {
599
+ param = parser.shallowDeref(param);
600
+ operationParamNames[param.name + "_" + param.in] = true;
601
+ });
602
+
603
+ // filter out path params overridden by operation ones with the same name
604
+ pathParams = pathParams.filter((param) => {
605
+ param = parser.shallowDeref(param);
606
+ return !operationParamNames[param.name + "_" + param.in];
607
+ });
608
+
609
+ return pathParams.concat(operationParams);
610
+ }
611
+
612
+ export function mergeSimilarMediaTypes(
613
+ types: Record<string, OpenAPIMediaType>
614
+ ): Record<string, OpenAPIMediaType> {
615
+ const mergedTypes = {};
616
+ Object.keys(types).forEach((name) => {
617
+ const mime = types[name];
618
+ // ignore content type parameters (e.g. charset) and merge
619
+ const normalizedMimeName = name.split(";")[0].trim();
620
+ if (!mergedTypes[normalizedMimeName]) {
621
+ mergedTypes[normalizedMimeName] = mime;
622
+ return;
623
+ }
624
+ mergedTypes[normalizedMimeName] = {
625
+ ...mergedTypes[normalizedMimeName],
626
+ ...mime,
627
+ };
628
+ });
629
+
630
+ return mergedTypes;
631
+ }
632
+
633
+ export function expandDefaultServerVariables(
634
+ url: string,
635
+ variables: object = {}
636
+ ) {
637
+ return url.replace(
638
+ /(?:{)([\w-.]+)(?:})/g,
639
+ (match, name) => (variables[name] && variables[name].default) || match
640
+ );
641
+ }
642
+
643
+ export function normalizeServers(
644
+ specUrl: string | undefined,
645
+ servers: OpenAPIServer[]
646
+ ): OpenAPIServer[] {
647
+ const getHref = () => {
648
+ if (!false) {
649
+ return "";
650
+ }
651
+ const href = window.location.href;
652
+ return href.endsWith(".html") ? dirname(href) : href;
653
+ };
654
+
655
+ const baseUrl =
656
+ specUrl === undefined ? removeQueryString(getHref()) : dirname(specUrl);
657
+
658
+ if (servers.length === 0) {
659
+ // Behaviour defined in OpenAPI spec: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#openapi-object
660
+ servers = [
661
+ {
662
+ url: "/",
663
+ },
664
+ ];
665
+ }
666
+
667
+ function normalizeUrl(url: string): string {
668
+ return resolveUrl(baseUrl, url);
669
+ }
670
+
671
+ return servers.map((server) => {
672
+ return {
673
+ ...server,
674
+ url: normalizeUrl(server.url),
675
+ description: server.description || "",
676
+ };
677
+ });
678
+ }
679
+
680
+ export const SECURITY_DEFINITIONS_JSX_NAME = "SecurityDefinitions";
681
+ export const SCHEMA_DEFINITION_JSX_NAME = "SchemaDefinition";
682
+
683
+ export let SECURITY_SCHEMES_SECTION_PREFIX = "section/Authentication/";
684
+ export function setSecuritySchemePrefix(prefix: string) {
685
+ SECURITY_SCHEMES_SECTION_PREFIX = prefix;
686
+ }
687
+
688
+ export const shortenHTTPVerb = (verb) =>
689
+ ({
690
+ delete: "del",
691
+ options: "opts",
692
+ }[verb] || verb);
693
+
694
+ export function isRedocExtension(key: string): boolean {
695
+ const redocExtensions = {
696
+ "x-circular-ref": true,
697
+ "x-code-samples": true, // deprecated
698
+ "x-codeSamples": true,
699
+ "x-displayName": true,
700
+ "x-examples": true,
701
+ "x-ignoredHeaderParameters": true,
702
+ "x-logo": true,
703
+ "x-nullable": true,
704
+ "x-servers": true,
705
+ "x-tagGroups": true,
706
+ "x-traitTag": true,
707
+ "x-additionalPropertiesName": true,
708
+ "x-explicitMappingOnly": true,
709
+ };
710
+
711
+ return key in redocExtensions;
712
+ }
713
+
714
+ export function extractExtensions(
715
+ obj: object,
716
+ showExtensions: string[] | true
717
+ ): Record<string, any> {
718
+ return Object.keys(obj)
719
+ .filter((key) => {
720
+ if (showExtensions === true) {
721
+ return key.startsWith("x-") && !isRedocExtension(key);
722
+ }
723
+ return key.startsWith("x-") && showExtensions.indexOf(key) > -1;
724
+ })
725
+ .reduce((acc, key) => {
726
+ acc[key] = obj[key];
727
+ return acc;
728
+ }, {});
729
+ }
730
+
731
+ export function pluralizeType(displayType: string): string {
732
+ return displayType
733
+ .split(" or ")
734
+ .map((type) =>
735
+ type.replace(
736
+ /^(string|object|number|integer|array|boolean)s?( ?.*)/,
737
+ "$1s$2"
738
+ )
739
+ )
740
+ .join(" or ");
741
+ }
742
+
743
+ export function getContentWithLegacyExamples(
744
+ info: OpenAPIRequestBody | OpenAPIResponse
745
+ ): { [mime: string]: OpenAPIMediaType } | undefined {
746
+ let mediaContent = info.content;
747
+ const xExamples = info["x-examples"]; // converted from OAS2 body param
748
+ const xExample = info["x-example"]; // converted from OAS2 body param
749
+
750
+ if (xExamples) {
751
+ mediaContent = { ...mediaContent };
752
+ for (const mime of Object.keys(xExamples)) {
753
+ const examples = xExamples[mime];
754
+ mediaContent[mime] = {
755
+ ...mediaContent[mime],
756
+ examples,
757
+ };
758
+ }
759
+ } else if (xExample) {
760
+ mediaContent = { ...mediaContent };
761
+ for (const mime of Object.keys(xExample)) {
762
+ const example = xExample[mime];
763
+ mediaContent[mime] = {
764
+ ...mediaContent[mime],
765
+ example,
766
+ };
767
+ }
768
+ }
769
+
770
+ return mediaContent;
771
+ }