rapidoc 9.1.2 → 9.1.3

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.
@@ -27,11 +27,11 @@
27
27
  <main>
28
28
  <h2>Introducing &lt;rapi-doc-mini&gt; Web Component </h2>
29
29
  <p>
30
- <b>&lt;rapi-doc-mini&gt;</b> a new way to expose your API documentation. There are various styles and need how one would like to document their APIs.
30
+ <b>&lt;rapi-doc-mini&gt;</b> a new way to expose your API documentation. There are various styles and needs how one would like to document their APIs.
31
31
  such as
32
32
  <ul>
33
33
  <li>
34
- All the APIs are documented in one central place. This is where <b>&lt;rapi-doc&gt;</b> fits in.
34
+ All the APIs are documented in one central place. This is where <b>&lt;rapi-doc&gt;</b> fits in. However for the rest of bullets listed below <b>&lt;rapi-doc-mini&gt;</b> is your friend
35
35
  </li>
36
36
  <li>
37
37
  Expose just a few important APIs such as authentication etc as a part of a blog, to provide an initial understanding to your audience
@@ -41,13 +41,12 @@
41
41
  </li>
42
42
  <li>
43
43
  Or you are explaining a business case which uses microservices,
44
- where each microservice has its own APIs and the ask is to pick few APIs from each of theses microservice show how they come work together in a single document
44
+ where each microservice has its own APIs and the ask is to pick few APIs from each of theses microservice show how they work together in a single document
45
45
  </li>
46
46
  </ul>
47
47
  </p>
48
48
 
49
-
50
- <h2>Usage in a bolg style writing</h2>
49
+ <h2>Usage in a blog style writing</h2>
51
50
  <p style="flex:1">
52
51
  It is fairly common to write specifically about few APIs to provide an initial understanding to your users.
53
52
  However in those cases you run the risk of your writeup getting outdated when the actual API changes its shape.
@@ -69,7 +68,8 @@
69
68
  bg-color = "#f9f9fb"
70
69
  &gt; &lt;/rapi-doc-mini&gt;</code>
71
70
  </pre>
72
- Above HTML snippet will result in generating an API doc as shown below. Go ahead and play with the below APIs, make live request and watch the responses
71
+ Above HTML snippet will result in generating an API doc as shown below. Notice how it blends seamlessly with this writeup as if its a part of this page.
72
+ Go ahead and play with the below APIs, make live request and watch the responses, all from this very blog.
73
73
  </p>
74
74
  <rapi-doc-mini
75
75
  style= "min-width:600px;"
@@ -95,9 +95,8 @@
95
95
  paths-expanded = "false"
96
96
  > </rapi-doc-mini>
97
97
 
98
-
99
98
  <br/><br/>
100
- <h3>Pick and show specific APIs </h3>
99
+ <h3>Pick only specific APIs from OpenAPI spec</h3>
101
100
  Instead of showing entire open-api spec you can just extract and choose to show what you need to show. This is done using <b>match-paths</b> attribute.
102
101
  <br/>Example below uses the same OpenAPI spec as we used above, but showing only the paths that we intend to
103
102
 
@@ -125,6 +124,7 @@
125
124
  > </rapi-doc-mini>
126
125
  <br/>
127
126
  <h3>More complex RegEx based filtering of API is possible too (match-paths & match-type)</h3>
127
+ Here we are using regular RegEx to select the paths which is either <span class="mono blue"> get /users/{userId}</span> &nbsp;&nbsp;or&nbsp;&nbsp; <span class="mono blue"> post /users</span>
128
128
  <pre class="shadow1 code-block" style="width:575px">
129
129
  <code class="language-html">
130
130
  &lt;rapi-doc-mini
@@ -152,7 +152,7 @@
152
152
  > </rapi-doc-mini>
153
153
 
154
154
  <h3>Show it Minimzed or Expanded</h3>
155
- If expand/collapse is not your style, or feel it a bit of distraction then have it always expanded and make it more natural and integrated part of your writeup.
155
+ If expand/collapse is not your style, then have it always expanded and make it more natural and integrated part of your writeup.
156
156
  <br> Use the attribute <b>paths-expanded = "true"</b> to achive this <br/><br/>
157
157
 
158
158
  <rapi-doc-mini
@@ -216,4 +216,4 @@
216
216
  </main>
217
217
 
218
218
  </body>
219
- </html>
219
+ </html>
@@ -0,0 +1,35 @@
1
+ openapi: 3.0.1
2
+ info:
3
+ title: Authentication Test Case
4
+ description: Demonstrate various authentication methods supported by RapiDoc
5
+ servers:
6
+ - url: https://reqres.in/api/
7
+ paths:
8
+ /users/{userId}:
9
+ get:
10
+ description: Uses HTTP Basic Auth
11
+ parameters:
12
+ - in: path
13
+ name: userId
14
+ schema:
15
+ type: integer
16
+ example: 3
17
+ required: true
18
+ description: Numeric ID of the user to get
19
+ security:
20
+ - http-basic: []
21
+
22
+ /users:
23
+ get:
24
+ description: Uses API Key Auth
25
+ security:
26
+ - api_key1: []
27
+ components:
28
+ securitySchemes:
29
+ http-basic:
30
+ type: http
31
+ scheme: basic
32
+ api_key1:
33
+ type: apiKey
34
+ name: Authorization
35
+ in: header
@@ -48,9 +48,9 @@ paths:
48
48
  - ms-oauth:
49
49
  - User.ReadBasic.All
50
50
  - Directory.Read.All
51
- - http-basic: []
51
+ - http-basic: ['h-scope1', 'h-scope2']
52
52
  - api_key_cookie: []
53
- - api_key1: []
53
+ - api_key1: ['a-scope1', 'a-scope2']
54
54
  api_key2: []
55
55
  components:
56
56
  securitySchemes:
@@ -75,6 +75,25 @@ paths:
75
75
  type: string
76
76
  additionalProperties:
77
77
  type: string
78
+ - in: query
79
+ name: dynamic-query-params5
80
+ explode: false
81
+ required: false
82
+ schema:
83
+ type: object
84
+ properties:
85
+ propertyOne:
86
+ type: string
87
+ enum:
88
+ - VALUE-1
89
+ - VALUE-2
90
+ default: VALUE-2
91
+ propertyTwo:
92
+ type: string
93
+ enum:
94
+ - FLAG-1
95
+ - FLAG-2
96
+ default: FLAG-1
78
97
  responses:
79
98
  '200':
80
99
  description: successful operation
@@ -187,6 +206,28 @@ paths:
187
206
  type: string
188
207
  additionalProperties:
189
208
  type: string
209
+ - in: query
210
+ name: dynamic-query-params5
211
+ description: >
212
+ Example generation works for non-exloded objects:<br />
213
+ `propertyOne=VALUE-1&property2=FLAG-1`
214
+ explode: false
215
+ required: false
216
+ schema:
217
+ type: object
218
+ properties:
219
+ propertyOne:
220
+ type: string
221
+ enum:
222
+ - VALUE-1
223
+ - VALUE-2
224
+ default: VALUE-2
225
+ propertyTwo:
226
+ type: string
227
+ enum:
228
+ - FLAG-1
229
+ - FLAG-2
230
+ default: FLAG-1
190
231
  responses:
191
232
  '200':
192
233
  description: successful operation
@@ -11,6 +11,8 @@ paths:
11
11
  multipart/form-data:
12
12
  schema:
13
13
  type: object
14
+ required:
15
+ - agePart
14
16
  properties:
15
17
  photoPart:
16
18
  type: string
@@ -27,6 +29,7 @@ paths:
27
29
  agePart:
28
30
  type: integer
29
31
  default: 21
32
+ deprecated: true
30
33
  description: 'Age of the person'
31
34
  contactPart:
32
35
  type: object
@@ -1,7 +1,7 @@
1
1
  openapi: 3.0.2
2
2
  info:
3
3
  version: '1.0'
4
- title: Server Variables
4
+ title: API Server templating using variables
5
5
  description: >
6
6
  Various ways to define API servers in the spec including variables
7
7
 
@@ -84,10 +84,10 @@ servers:
84
84
  - v1
85
85
  - v2
86
86
  paths:
87
- /server-variables:
87
+ /api-server-template:
88
88
  get:
89
- summary: Server Variable.
90
- discription: Computed server url should be shown against `API SERVER`.
89
+ summary: Dynamic API Server.
90
+ description: Uses API server template string and variables which can be used to generatea a desired API server url - The selected and computed API Server URL should be shown against `API SERVER`.
91
91
  responses:
92
92
  '200':
93
93
  description: successful operation
package/index.html CHANGED
@@ -37,7 +37,9 @@
37
37
  schema-style="tree"
38
38
  show-components = "true"
39
39
  show-info = "true"
40
- show-header = "false"
40
+ show-header = "true"
41
+ allow-spec-url-load="false"
42
+ allow-spec-file-load="false"
41
43
  allow-search = "true"
42
44
  allow-advanced-search = "false"
43
45
  allow-server-selection = "false"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rapidoc",
3
- "version": "9.1.2",
3
+ "version": "9.1.3",
4
4
  "description": "RapiDoc - Open API spec viewer with built in console",
5
5
  "author": "Mrinmoy Majumdar <mrin9@yahoo.com>",
6
6
  "repository": {
@@ -64,12 +64,12 @@
64
64
  "filemanager-webpack-plugin": "^6.1.7",
65
65
  "html-webpack-plugin": "^5.3.2",
66
66
  "inspectpack": "^4.7.1",
67
- "style-loader": "^3.2.1",
67
+ "style-loader": "^3.3.0",
68
68
  "terser-webpack-plugin": "^5.2.4",
69
- "webpack": "^5.53.0",
69
+ "webpack": "^5.54.0",
70
70
  "webpack-bundle-analyzer": "^4.4.2",
71
71
  "webpack-cli": "^4.8.0",
72
- "webpack-dev-server": "^4.2.1"
72
+ "webpack-dev-server": "^4.3.0"
73
73
  },
74
74
  "engines": {
75
75
  "node": ">=10.21.0"
@@ -102,6 +102,9 @@ export default class ApiRequest extends LitElement {
102
102
  color: var(--fg);
103
103
  font-family: var(--font-mono);
104
104
  }
105
+ .param-name.deprecated {
106
+ text-decoration: line-through;
107
+ }
105
108
  .param-type{
106
109
  color: var(--light-fg);
107
110
  font-family: var(--font-regular);
@@ -308,7 +311,7 @@ export default class ApiRequest extends LitElement {
308
311
  '',
309
312
  '',
310
313
  declaredParamSchema,
311
- serializeStyle,
314
+ serializeStyle || 'json',
312
315
  true,
313
316
  true,
314
317
  'text',
@@ -581,9 +584,7 @@ export default class ApiRequest extends LitElement {
581
584
  data-example-format = "${v.exampleFormat}"
582
585
  style="width:100%; resize:vertical;"
583
586
  .textContent = "${this.fillRequestFieldsWithExample === 'true' ? (v.exampleFormat === 'text' ? v.exampleValue : JSON.stringify(v.exampleValue, null, 2)) : ''}"
584
- >
585
- </textarea>
586
-
587
+ ></textarea>
587
588
  </div>
588
589
  `)}
589
590
 
@@ -766,11 +767,8 @@ export default class ApiRequest extends LitElement {
766
767
  formDataTableRows.push(html`
767
768
  <tr>
768
769
  <td style="width:${labelColWidth}; min-width:100px;">
769
- <div class="param-name">
770
- ${fieldSchema.required
771
- ? html`<span style='color:var(--red);'>*</span>${fieldName}`
772
- : html`${fieldName}`
773
- }
770
+ <div class="param-name ${fieldSchema.deprecated ? 'deprecated' : ''}">
771
+ ${fieldName}${(schema.required?.includes(fieldName) || fieldSchema.required) ? html`<span style='color:var(--red);'>*</span>` : ''}
774
772
  </div>
775
773
  <div class="param-type">${paramSchema.type}</div>
776
774
  </td>
@@ -935,8 +933,7 @@ export default class ApiRequest extends LitElement {
935
933
  <pre style="white-space:pre; max-height:400px; overflow:auto">${responseFormat
936
934
  ? html`<code>${unsafeHTML(Prism.highlight(this.responseText, Prism.languages[responseFormat], responseFormat))}</code>`
937
935
  : `${this.responseText}`
938
- }
939
- </pre>
936
+ }</pre>
940
937
  </div>`
941
938
  }
942
939
  <div class="tab-content col m-markdown" style="flex:1;display:${this.activeResponseTab === 'headers' ? 'flex' : 'none'};" >
@@ -1057,7 +1054,6 @@ export default class ApiRequest extends LitElement {
1057
1054
  fetchUrl = this.path;
1058
1055
  const fetchOptions = {
1059
1056
  method: this.method.toUpperCase(),
1060
- headers: {},
1061
1057
  };
1062
1058
  // Generate URL using Path Params
1063
1059
  pathParamEls.map((el) => {
@@ -1154,13 +1150,13 @@ export default class ApiRequest extends LitElement {
1154
1150
  curlUrl = fetchUrl;
1155
1151
  }
1156
1152
  curl = `curl -X ${this.method.toUpperCase()} "${curlUrl}" \\\n`;
1157
-
1153
+ const reqHeaders = new Headers();
1158
1154
  if (acceptHeader) {
1159
1155
  // Uses the acceptHeader from Response panel
1160
- fetchOptions.headers.Accept = acceptHeader;
1156
+ reqHeaders.append('Accept', acceptHeader);
1161
1157
  curlHeaders += ` -H "Accept: ${acceptHeader}" \\\n`;
1162
1158
  } else if (this.accept) {
1163
- fetchOptions.headers.Accept = this.accept;
1159
+ reqHeaders.append('Accept', this.accept);
1164
1160
  curlHeaders += ` -H "Accept: ${this.accept}" \\\n`;
1165
1161
  }
1166
1162
 
@@ -1168,14 +1164,14 @@ export default class ApiRequest extends LitElement {
1168
1164
  this.api_keys
1169
1165
  .filter((v) => (v.in === 'header'))
1170
1166
  .forEach((v) => {
1171
- fetchOptions.headers[v.name] = v.finalKeyValue;
1167
+ reqHeaders.append(v.name, v.finalKeyValue);
1172
1168
  curlHeaders += ` -H "${v.name}: ${v.finalKeyValue}" \\\n`;
1173
1169
  });
1174
1170
 
1175
1171
  // Add Header Params
1176
1172
  headerParamEls.map((el) => {
1177
1173
  if (el.value) {
1178
- fetchOptions.headers[el.dataset.pname] = el.value;
1174
+ reqHeaders.append(el.dataset.pname, el.value);
1179
1175
  curlHeaders += ` -H "${el.dataset.pname}: ${el.value}" \\\n`;
1180
1176
  }
1181
1177
  });
@@ -1257,18 +1253,22 @@ export default class ApiRequest extends LitElement {
1257
1253
  const exampleTextAreaEl = requestPanelEl.querySelector('.request-body-param-user-input');
1258
1254
  if (exampleTextAreaEl?.value) {
1259
1255
  fetchOptions.body = exampleTextAreaEl.value;
1260
- // curlData = ` -d ${JSON.stringify(exampleTextAreaEl.value.replace(/(\r\n|\n|\r)/gm, '')).replace(/\\"/g, "'")} \\ \n`;
1261
- try {
1262
- curlData = ` -d '${JSON.stringify(JSON.parse(exampleTextAreaEl.value))}' \\\n`;
1263
- } catch (err) {
1264
- curlData = ` -d '${exampleTextAreaEl.value.replace(/(\r\n|\n|\r)/gm, '')}' \\\n`;
1256
+ if (requestBodyType.includes('json')) {
1257
+ try {
1258
+ curlData = ` -d '${JSON.stringify(JSON.parse(exampleTextAreaEl.value))}' \\\n`;
1259
+ } catch (err) {
1260
+ // Ignore.
1261
+ }
1262
+ }
1263
+ if (!curlData) {
1264
+ curlData = ` -d '${exampleTextAreaEl.value.replace(/'/g, '\'"\'"\'')}' \\\n`;
1265
1265
  }
1266
1266
  }
1267
1267
  }
1268
1268
  // Common for all request-body
1269
1269
  if (!requestBodyType.includes('form-data')) {
1270
1270
  // For multipart/form-data dont set the content-type to allow creation of browser generated part boundaries
1271
- fetchOptions.headers['Content-Type'] = requestBodyType;
1271
+ reqHeaders.append('Content-Type', requestBodyType);
1272
1272
  }
1273
1273
  curlHeaders += ` -H "Content-Type: ${requestBodyType}" \\\n`;
1274
1274
  }
@@ -1287,6 +1287,7 @@ export default class ApiRequest extends LitElement {
1287
1287
  if (this.fetchCredentials) {
1288
1288
  fetchOptions.credentials = this.fetchCredentials;
1289
1289
  }
1290
+ fetchOptions.headers = reqHeaders;
1290
1291
  const fetchRequest = new Request(fetchUrl, fetchOptions);
1291
1292
  this.dispatchEvent(new CustomEvent('before-try', {
1292
1293
  bubbles: true,
@@ -99,7 +99,7 @@ export default class JsonTree extends LitElement {
99
99
  <div class="inside-bracket">
100
100
  ${Object.keys(data).map((key, i, a) => html`
101
101
  <div class="item">
102
- ${detailType === 'pure_object' ? html`${key}:` : ''}
102
+ ${detailType === 'pure_object' ? html`"${key}":` : ''}
103
103
  ${this.generateTree(data[key], i === (a.length - 1))}
104
104
  </div>`)
105
105
  }
@@ -55,6 +55,7 @@ export default class JsonSchemaViewer extends LitElement {
55
55
  // Allow or restrict features
56
56
  allowSpecUrlLoad: { type: String, attribute: 'allow-spec-url-load' },
57
57
  allowSpecFileLoad: { type: String, attribute: 'allow-spec-file-load' },
58
+ allowSpecFileDownload: { type: String, attribute: 'allow-spec-file-download' },
58
59
  allowSearch: { type: String, attribute: 'allow-search' },
59
60
 
60
61
  // Main Colors and Font
package/src/rapidoc.js CHANGED
@@ -88,6 +88,7 @@ export default class RapiDoc extends LitElement {
88
88
  allowTry: { type: String, attribute: 'allow-try' },
89
89
  allowSpecUrlLoad: { type: String, attribute: 'allow-spec-url-load' },
90
90
  allowSpecFileLoad: { type: String, attribute: 'allow-spec-file-load' },
91
+ allowSpecFileDownload: { type: String, attribute: 'allow-spec-file-download' },
91
92
  allowSearch: { type: String, attribute: 'allow-search' },
92
93
  allowAdvancedSearch: { type: String, attribute: 'allow-advanced-search' },
93
94
  allowServerSelection: { type: String, attribute: 'allow-server-selection' },
@@ -47,7 +47,7 @@ export default function overviewTemplate() {
47
47
  ? html`<span><a href="${this.resolvedSpec.info.termsOfService}" part="anchor anchor-overview">Terms of Service</a></span>`
48
48
  : ''
49
49
  }
50
- ${this.specUrl
50
+ ${this.specUrl && this.allowSpecFileDownload === 'true'
51
51
  ? html`
52
52
  <div style="display:flex; margin:12px 0; gap:8px; justify-content: start;">
53
53
  <button class="m-btn thin-border" style="width:170px" part="btn btn-outline" @click='${(e) => { downloadResource(this.specUrl, 'openapi-spec', e); }}'>Download OpenAPI spec</button>
@@ -1,3 +1,4 @@
1
+ /* eslint-disable arrow-body-style */
1
2
  import { html } from 'lit-element';
2
3
  import { unsafeHTML } from 'lit-html/directives/unsafe-html';
3
4
  import marked from 'marked';
@@ -449,19 +450,18 @@ export function pathSecurityTemplate(pathSecurity) {
449
450
  pathSecurity.forEach((pSecurity) => {
450
451
  const andSecurityKeys1 = [];
451
452
  const andKeyTypes = [];
452
- let pathScopes = '';
453
453
  Object.keys(pSecurity).forEach((pathSecurityKey) => {
454
+ let pathScopes = '';
454
455
  const s = this.resolvedSpec.securitySchemes.find((ss) => ss.securitySchemeId === pathSecurityKey);
455
- if (!pathScopes && Array.isArray(pSecurity[pathSecurityKey])) {
456
+ if (pSecurity[pathSecurityKey] && Array.isArray(pSecurity[pathSecurityKey])) {
456
457
  pathScopes = pSecurity[pathSecurityKey].join(', ');
457
458
  }
458
459
  if (s) {
459
460
  andKeyTypes.push(s.typeDisplay);
460
- andSecurityKeys1.push(s);
461
+ andSecurityKeys1.push({ ...s, ...({ scopes: pathScopes }) });
461
462
  }
462
463
  });
463
464
  orSecurityKeys1.push({
464
- pathScopes,
465
465
  securityTypes: andKeyTypes.length > 1 ? `${andKeyTypes[0]} + ${andKeyTypes.length - 1} more` : andKeyTypes[0],
466
466
  securityDefs: andSecurityKeys1,
467
467
  });
@@ -485,7 +485,20 @@ export function pathSecurityTemplate(pathSecurity) {
485
485
  <div class="tooltip-text" style="position:absolute; color: var(--fg); top:26px; right:0; border:1px solid var(--border-color);padding:2px 4px; display:block;">
486
486
  ${orSecurityItem1.securityDefs.length > 1 ? html`<div>Requires <b>all</b> of the following </div>` : ''}
487
487
  <div style="padding-left: 8px">
488
- ${orSecurityItem1.securityDefs.map((andSecurityItem, j) => html`
488
+ ${orSecurityItem1.securityDefs.map((andSecurityItem, j) => {
489
+ const scopeHtml = html`${andSecurityItem.scopes !== ''
490
+ ? html`
491
+ <div>
492
+ <b>Required scopes:</b>
493
+ <br/>
494
+ <div style="margin-left:8px">
495
+ ${andSecurityItem.scopes.split(',').map((scope, cnt) => html`${cnt === 0 ? '' : '┃'}<span>${scope}</span>`)}
496
+ </div>
497
+ </div>`
498
+ : ''
499
+ }`;
500
+
501
+ return html`
489
502
  ${andSecurityItem.type === 'oauth2'
490
503
  ? html`
491
504
  <div>
@@ -494,31 +507,23 @@ export function pathSecurityTemplate(pathSecurity) {
494
507
  : 'Needs'
495
508
  }
496
509
  OAuth Token <span style="font-family:var(--font-mono); color:var(--primary-color);">${andSecurityItem.securitySchemeId}</span> in <b>Authorization header</b>
497
- ${orSecurityItem1.pathScopes !== ''
498
- ? html`
499
- <div>
500
- <b>Required scopes:</b>
501
- <br/>
502
- <div style="margin-left:8px">
503
- ${orSecurityItem1.pathScopes.split(',').map((scope, cnt) => html`${cnt === 0 ? '' : '┃'}<span>${scope}</span>`)}
504
- </div>
505
- </div>`
506
- : ''
507
- }
510
+ ${scopeHtml}
508
511
  </div>`
509
512
  : andSecurityItem.type === 'http'
510
513
  ? html`
511
514
  <div>
512
515
  ${orSecurityItem1.securityDefs.length > 1 ? html`<b>${j + 1}.</b> &nbsp;` : html`Requires`}
513
516
  ${andSecurityItem.scheme === 'basic' ? 'Base 64 encoded username:password' : 'Bearer Token'} in <b>Authorization header</b>
517
+ ${scopeHtml}
514
518
  </div>`
515
519
  : html`
516
520
  <div>
517
521
  ${orSecurityItem1.securityDefs.length > 1 ? html`<b>${j + 1}.</b> &nbsp;` : html`Requires`}
518
522
  Token in <b>${andSecurityItem.name} ${andSecurityItem.in}</b>
523
+ ${scopeHtml}
519
524
  </div>`
520
- }
521
- `)}
525
+ }`;
526
+ })}
522
527
  </div>
523
528
  </div>
524
529
  </div>
@@ -661,7 +661,7 @@ export function generateExample(examples = '', example = '', schema, mimeType, i
661
661
  for (const eg in examples) {
662
662
  let egContent = '';
663
663
  let egFormat = 'json';
664
- if (mimeType.toLowerCase().includes('json')) {
664
+ if (mimeType?.toLowerCase().includes('json')) {
665
665
  if (outputType === 'text') {
666
666
  egContent = typeof examples[eg].value === 'string' ? examples[eg].value : JSON.stringify(examples[eg].value, undefined, 2);
667
667
  egFormat = 'text';
@@ -695,7 +695,7 @@ export function generateExample(examples = '', example = '', schema, mimeType, i
695
695
  } else if (example) {
696
696
  let egContent = '';
697
697
  let egFormat = 'json';
698
- if (mimeType.toLowerCase().includes('json')) {
698
+ if (mimeType?.toLowerCase().includes('json')) {
699
699
  if (outputType === 'text') {
700
700
  egContent = typeof example === 'string' ? example : JSON.stringify(example, undefined, 2);
701
701
  egFormat = 'text';
@@ -734,14 +734,14 @@ export function generateExample(examples = '', example = '', schema, mimeType, i
734
734
  exampleDescription: '',
735
735
  exampleType: mimeType,
736
736
  exampleValue: schema.example,
737
- exampleFormat: ((mimeType.toLowerCase().includes('json') && typeof schema.example === 'object') ? 'json' : 'text'),
737
+ exampleFormat: ((mimeType?.toLowerCase().includes('json') && typeof schema.example === 'object') ? 'json' : 'text'),
738
738
  });
739
- } else if (mimeType.toLowerCase().includes('json') || mimeType.toLowerCase().includes('text') || mimeType.toLowerCase().includes('*/*') || mimeType.toLowerCase().includes('xml')) {
739
+ } else if (mimeType?.toLowerCase().includes('json') || mimeType?.toLowerCase().includes('text') || mimeType?.toLowerCase().includes('*/*') || mimeType?.toLowerCase().includes('xml')) {
740
740
  let xmlRootStart = '';
741
741
  let xmlRootEnd = '';
742
742
  let exampleFormat = '';
743
743
  let exampleValue = '';
744
- if (mimeType.toLowerCase().includes('xml')) {
744
+ if (mimeType?.toLowerCase().includes('xml')) {
745
745
  xmlRootStart = (schema.xml && schema.xml.name) ? `<${schema.xml.name}>` : '<root>';
746
746
  xmlRootEnd = (schema.xml && schema.xml.name) ? `</${schema.xml.name}>` : '</root>';
747
747
  exampleFormat = 'text';
@@ -767,7 +767,7 @@ export function generateExample(examples = '', example = '', schema, mimeType, i
767
767
  const description = samples[samplesKey]['::DESCRIPTION'] || '';
768
768
  removeTitlesAndDescriptions(samples[samplesKey]);
769
769
 
770
- if (mimeType.toLowerCase().includes('xml')) {
770
+ if (mimeType?.toLowerCase().includes('xml')) {
771
771
  exampleValue = `${xmlRootStart}${json2xml(samples[samplesKey])}\n${xmlRootEnd}`;
772
772
  } else {
773
773
  exampleValue = outputType === 'text' ? JSON.stringify(samples[samplesKey], null, 2) : samples[samplesKey];
@@ -819,7 +819,7 @@ function getSerializeStyleForContentType(contentType) {
819
819
 
820
820
  export function getSchemaFromParam(param) {
821
821
  if (param.schema) {
822
- return [param.schema, null];
822
+ return [param.schema, null, null];
823
823
  }
824
824
  if (param.content) {
825
825
  // we gonna use the first content-encoding
@@ -829,5 +829,5 @@ export function getSchemaFromParam(param) {
829
829
  }
830
830
  }
831
831
  }
832
- return [null, null];
832
+ return [null, null, null];
833
833
  }
@@ -331,6 +331,14 @@ function groupByTags(openApiSpec, generateMissingTags = false, sortTags = false,
331
331
  finalParameters = pathOrHookObj.parameters ? pathOrHookObj.parameters.slice(0) : [];
332
332
  }
333
333
 
334
+ // Filter callbacks to contain only objects.
335
+ if (pathOrHookObj.callbacks) {
336
+ for (const [callbackName, callbackConfig] of Object.entries(pathOrHookObj.callbacks)) {
337
+ const filteredCallbacks = Object.entries(callbackConfig).filter((entry) => typeof entry[1] === 'object') || [];
338
+ pathOrHookObj.callbacks[callbackName] = Object.fromEntries(filteredCallbacks);
339
+ }
340
+ }
341
+
334
342
  // Update Responses
335
343
  tagObj.paths.push({
336
344
  show: true,
package/webpack.config.js CHANGED
@@ -49,18 +49,6 @@ const commonPlugins = [
49
49
  },
50
50
  },
51
51
  }),
52
- /*
53
- new ESLintPlugin({
54
- emitError: true,
55
- emitWarning: true,
56
- formatter: 'stylish',
57
- overrideConfigFile: path.resolve(__dirname, '.eslintrc'),
58
- outputReport: {
59
- filePath: './eslint_report.html',
60
- formatter: 'html',
61
- },
62
- }),
63
- */
64
52
  ];
65
53
 
66
54
  if (process.env.NODE_ENV === 'production') {