openapi-explorer 0.6.218 → 0.6.223

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openapi-explorer",
3
- "version": "0.6.218",
3
+ "version": "0.6.223",
4
4
  "description": "OpenAPI Explorer - API viewer with dynamically generated components, documentation, and interaction console",
5
5
  "author": "Rhosys Developers <developers@rhosys.ch>",
6
6
  "repository": {
@@ -144,9 +144,9 @@ export default class ApiRequest extends LitElement {
144
144
 
145
145
  tableRows.push(html`
146
146
  <tr>
147
- <td rowspan="${this.allowTry === 'true' ? '1' : '2'}" style="width:160px; min-width:50px;">
148
- <div class="param-name">
149
- ${param.required ? html`<span style='color:var(--red)'>*</span>` : ''}${param.name}
147
+ <td style="width:160px; min-width:50px;">
148
+ <div class="param-name ${paramSchema.deprecated ? 'deprecated' : ''}">
149
+ ${param.name}${!paramSchema.deprecated && param.required ? html`<span style='color:var(--red);'>*</span>` : ''}
150
150
  </div>
151
151
  <div class="param-type">
152
152
  ${paramSchema.type === 'array'
@@ -203,7 +203,7 @@ export default class ApiRequest extends LitElement {
203
203
  }
204
204
  ${this.renderStyle === 'focused'
205
205
  ? html`
206
- <td colspan="${(this.allowTry === 'true') ? '1' : '2'}">
206
+ <td>
207
207
  ${paramSchema.default || paramSchema.constrain || paramSchema.allowedValues || paramSchema.pattern
208
208
  ? html`
209
209
  <div class="param-constraint">
@@ -497,11 +497,8 @@ export default class ApiRequest extends LitElement {
497
497
  formDataTableRows.push(html`
498
498
  <tr>
499
499
  <td style="width:160px; min-width:100px;">
500
- <div class="param-name">
501
- ${fieldSchema.required
502
- ? html`<span style='color:var(--red);'>*</span>${fieldName}`
503
- : html`${fieldName}`
504
- }
500
+ <div class="param-name ${fieldSchema.deprecated ? 'deprecated' : ''}">
501
+ ${fieldName}${!fieldSchema.deprecated && (schema.required?.includes(fieldName) || fieldSchema.required) ? html`<span style='color:var(--red);'>*</span>` : ''}
505
502
  </div>
506
503
  <div class="param-type">${paramSchema.type}</div>
507
504
  </td>
@@ -1004,11 +1001,15 @@ export default class ApiRequest extends LitElement {
1004
1001
  const exampleTextAreaEl = requestPanelEl.querySelector('.request-body-param-user-input');
1005
1002
  if (exampleTextAreaEl && exampleTextAreaEl.value) {
1006
1003
  fetchOptions.body = exampleTextAreaEl.value;
1007
- // curlData = ` -d ${JSON.stringify(exampleTextAreaEl.value.replace(/(\r\n|\n|\r)/gm, '')).replace(/\\"/g, "'")} \\ \n`;
1008
- try {
1009
- curlData = ` -d '${JSON.stringify(JSON.parse(exampleTextAreaEl.value))}' \\\n`;
1010
- } catch (err) {
1011
- curlData = ` -d '${exampleTextAreaEl.value.replace(/(\r\n|\n|\r)/gm, '')}' \\\n`;
1004
+ if (requestBodyType.includes('json')) {
1005
+ try {
1006
+ curlData = ` -d '${JSON.stringify(JSON.parse(exampleTextAreaEl.value))}' \\\n`;
1007
+ } catch (err) { /* Ignore unparseable JSON */ }
1008
+ }
1009
+
1010
+ if (!curlData) {
1011
+ // Save single quotes wrapped => 'text' => `"'"text"'"`
1012
+ curlData = ` -d '${exampleTextAreaEl.value.replace(/'/g, '\'"\'"\'')}' \\\n`;
1012
1013
  }
1013
1014
  }
1014
1015
  }
@@ -199,23 +199,6 @@ export default class OpenApiExplorer extends LitElement {
199
199
  background-image: linear-gradient(to right, rgba(0,0,0,0), var(--border-color), rgba(0,0,0,0));
200
200
  }
201
201
 
202
- .section-tag-header:hover::after {
203
- position:absolute;
204
- margin-left:-24px;
205
- font-size:20px;
206
- top: calc(50% - 14px);
207
- color:var(--primary-color);
208
- content: '⬆';
209
- }
210
-
211
- .collapsed .section-tag-header::after {
212
- position:absolute;
213
- margin-left:-24px;
214
- font-size:20px;
215
- top: calc(50% - 14px);
216
- color: var(--border-color);
217
- content: '⬇';
218
- }
219
202
  .collapsed .section-tag-header:hover::after {
220
203
  color:var(--primary-color);
221
204
  }
@@ -312,6 +295,17 @@ export default class OpenApiExplorer extends LitElement {
312
295
  100% { transform: rotate(360deg); }
313
296
  }
314
297
 
298
+ @media only screen and (max-width: 767.98px) {
299
+ .section-padding {
300
+ // margin-right: 1rem;
301
+ margin: 1rem;
302
+ }
303
+
304
+ .sub-title.tag {
305
+ margin-left: 1rem;
306
+ }
307
+ }
308
+
315
309
  @media only screen and (min-width: 768px) {
316
310
  .nav-bar {
317
311
  width: 260px;
@@ -19,6 +19,9 @@ export default css`
19
19
  color: var(--fg);
20
20
  font-family: var(--font-mono);
21
21
  }
22
+ .api-request .param-name.deprecated {
23
+ text-decoration: line-through;
24
+ }
22
25
  .api-request .param-type {
23
26
  color: var(--light-fg);
24
27
  font-family: var(--font-regular);
@@ -63,6 +63,9 @@ export default css`
63
63
  h1,h2,h3,h4,h5,h5{
64
64
  margin-block-end: 0.2em;
65
65
  }
66
+ h3 {
67
+ margin-top: 0;
68
+ }
66
69
  p { margin-block-start: 0.5em; }
67
70
  a { color: var(--blue); cursor: pointer; }
68
71
  a.inactive-link {
@@ -21,21 +21,33 @@ function toggleExpand(path) {
21
21
  this.requestUpdate();
22
22
  }
23
23
 
24
- export function expandCollapseAll(operationsRootEl, action = 'expand-all') {
25
- const elList = [...operationsRootEl.querySelectorAll('.section-tag')];
26
- if (action === 'expand-all') {
27
- elList.map((el) => {
28
- el.classList.replace('collapsed', 'expanded');
29
- });
24
+ function toggleTag(tagElement, tagId) {
25
+ const sectionTag = tagElement.target.closest('.section-tag');
26
+ const tag = this.resolvedSpec.tags.find(t => t.elementId === tagId);
27
+ tag.expanded = !tag.expanded;
28
+ if (tag.expanded) {
29
+ sectionTag.classList.remove('collapsed');
30
+ sectionTag.classList.add('expanded');
30
31
  } else {
31
- elList.map((el) => {
32
- el.classList.replace('expanded', 'collapsed');
33
- });
32
+ sectionTag.classList.remove('expanded');
33
+ sectionTag.classList.add('collapsed');
34
34
  }
35
+ this.requestUpdate();
35
36
  }
36
-
37
- function onExpandCollapseAll(e, action = 'expand-all') {
38
- expandCollapseAll.call(this, e.target.closest('.operations-root'), action);
37
+ export function expandCollapseAll(currentElement, action = 'expand-all') {
38
+ const operationsRootEl = currentElement.target.closest('.operations-root');
39
+ const elList = [...operationsRootEl.querySelectorAll('.section-tag')];
40
+ const expand = action === 'expand-all';
41
+ this.resolvedSpec.tags.forEach(t => t.expanded = expand);
42
+ elList.map((el) => {
43
+ if (expand) {
44
+ el.classList.remove('collapsed');
45
+ el.classList.add('expanded');
46
+ } else {
47
+ el.classList.remove('expanded');
48
+ el.classList.add('collapsed');
49
+ }
50
+ });
39
51
  }
40
52
 
41
53
  /* eslint-disable indent */
@@ -127,21 +139,25 @@ function endpointBodyTemplate(path) {
127
139
  export default function endpointTemplate() {
128
140
  return html`
129
141
  <div style="display:flex; justify-content:flex-end; padding-right: 1rem; font-size: 14px;">
130
- <span @click="${(e) => onExpandCollapseAll(e, 'expand-all')}" style="color:var(--primary-color); cursor: pointer;">Expand</span>
142
+ <span @click="${(e) => expandCollapseAll.call(this, e, 'expand-all')}" style="color:var(--primary-color); cursor: pointer;">Expand</span>
131
143
  &nbsp;|&nbsp;
132
- <span @click="${(e) => onExpandCollapseAll(e, 'collapse-all')}" style="color:var(--primary-color); cursor: pointer;">Collapse</span>
144
+ <span @click="${(e) => expandCollapseAll.call(this, e, 'collapse-all')}" style="color:var(--primary-color); cursor: pointer;">Collapse</span>
133
145
  </div>
134
146
  ${(this.resolvedSpec && this.resolvedSpec.tags || []).map((tag) => html`
135
147
  <div class='regular-font section-gap section-tag ${tag.expanded ? 'expanded' : 'collapsed'}' >
136
148
 
137
- <div class='section-tag-header' @click="${() => { tag.expanded = !tag.expanded; this.requestUpdate(); }}">
149
+ <div class='section-tag-header' @click="${(e) => toggleTag.call(this, e, tag.elementId)}">
138
150
  <div id='${tag.elementId}' class="sub-title tag" style="color:var(--primary-color)">${tag.name}</div>
139
151
  </div>
140
152
  <div class='section-tag-body'>
141
153
  <slot name="${tag.elementId}"></slot>
142
- <div class="regular-font regular-font-size m-markdown" style="padding-bottom:12px">
143
- ${unsafeHTML(marked(tag.description || ''))}
144
- </div>
154
+ ${tag.description
155
+ ? `html
156
+ <div class="regular-font regular-font-size m-markdown" style="padding-bottom:12px">
157
+ ${unsafeHTML(marked(tag.description || ''))}
158
+ </div>`
159
+ : ''
160
+ }
145
161
  ${tag.paths.filter((v) => {
146
162
  if (this.matchPaths) {
147
163
  return pathIsInSearch(this.matchPaths, v);
@@ -5,11 +5,13 @@ import { pathIsInSearch } from '../utils/common-utils';
5
5
  export function expandCollapseNavBarTag(navLinkEl, action = 'toggle') {
6
6
  const tagAndPathEl = navLinkEl && navLinkEl.closest('.nav-bar-tag-and-paths');
7
7
  if (tagAndPathEl) {
8
- const isExpanded = tagAndPathEl.classList.contains('expanded');
9
- if (isExpanded && (action === 'toggle' || action === 'collapse')) {
10
- tagAndPathEl.classList.replace('expanded', 'collapsed');
11
- } else if (!isExpanded && (action === 'toggle' || action === 'expand')) {
12
- tagAndPathEl.classList.replace('collapsed', 'expanded');
8
+ const expand = tagAndPathEl.classList.contains('collapsed') && action === 'toggle' || action === 'expand';
9
+ if (expand) {
10
+ tagAndPathEl.classList.remove('collapsed');
11
+ tagAndPathEl.classList.add('expanded');
12
+ } else {
13
+ tagAndPathEl.classList.remove('expanded');
14
+ tagAndPathEl.classList.add('collapsed');
13
15
  }
14
16
  }
15
17
  }
@@ -18,11 +20,11 @@ export function expandCollapseAll(navEl, action = 'expand-all') {
18
20
  const elList = [...navEl.querySelectorAll('.nav-bar-tag-and-paths')];
19
21
  if (action === 'expand-all') {
20
22
  elList.map((el) => {
21
- el.classList.replace('collapsed', 'expanded');
23
+ expandCollapseNavBarTag(el, 'expand');
22
24
  });
23
25
  } else {
24
26
  elList.map((el) => {
25
- el.classList.replace('expanded', 'collapsed');
27
+ expandCollapseNavBarTag(el, 'collapse');
26
28
  });
27
29
  }
28
30
  }
@@ -16,7 +16,7 @@ export default function overviewTemplate() {
16
16
  ${this.resolvedSpec && this.resolvedSpec.info
17
17
  ? html`
18
18
  <slot name="overview">
19
- <div id="api-title" part="label-overview-title" style="font-size:32px">
19
+ <div id="api-title" part="label-overview-title" style="font-size:32px;" class="section-padding">
20
20
  ${this.resolvedSpec.info.title}
21
21
  ${!this.resolvedSpec.info.version ? '' : html`
22
22
  <span style = 'font-size:var(--font-size-small);font-weight:bold'>
@@ -24,7 +24,7 @@ export default function overviewTemplate() {
24
24
  </span>`
25
25
  }
26
26
  </div>
27
- <div id="api-info" style="font-size:calc(var(--font-size-regular) - 1px); margin-top:8px;">
27
+ <div id="api-info" style="font-size:calc(var(--font-size-regular) - 1px); margin-top:8px;" class="section-padding">
28
28
  ${this.resolvedSpec.info.contact && this.resolvedSpec.info.contact.email
29
29
  ? html`<span>${this.resolvedSpec.info.contact.name || 'Email'}:
30
30
  <a href="mailto:${this.resolvedSpec.info.contact.email}" part="anchor anchor-overview">${this.resolvedSpec.info.contact.email}</a>
@@ -53,7 +53,7 @@ export default function overviewTemplate() {
53
53
  ${this.resolvedSpec.info.description
54
54
  ? html`${
55
55
  unsafeHTML(`
56
- <div class="m-markdown regular-font">
56
+ <div class="m-markdown regular-font section-padding">
57
57
  ${marked(this.resolvedSpec.info.description, this.infoDescriptionHeadingsInNavBar === 'true' ? { renderer: headingRenderer() } : undefined)}
58
58
  </div>`)}`
59
59
  : ''
@@ -284,101 +284,103 @@ export default function securitySchemeTemplate() {
284
284
  return html`
285
285
  <section id='auth' part="section-auth" class = 'observe-me ${this.renderStyle === 'read' ? 'section-gap--read-mode' : (this.renderStyle === 'focused' ? 'section-gap--focused-mode' : 'section-gap')}'>
286
286
  <slot name="authentication">
287
- <div class='sub-title regular-font'> AUTHENTICATION </div>
288
- <div class="small-font-size" style="display:flex; align-items: center; min-height:30px">
289
- ${providedApiKeys.length > 0
290
- ? html`
291
- <div class="blue-text"> ${providedApiKeys.length} API key applied </div>
292
- <div style="flex:1"></div>
293
- <button class="m-btn thin-border" part="btn btn-outline" @click=${() => { onClearAllApiKeys.call(this); }}>CLEAR ALL API KEYS</button>`
294
- : html`<div class="red-text">No API key applied</div>`
295
- }
296
- </div>
297
- ${schemes.length > 0
298
- ? html`
299
- <table class='m-table' style = "width:100%">
300
- ${schemes.map((v) => html`
301
- <tr>
302
- <td style="max-width:500px; overflow-wrap: break-word;">
303
- <div style="min-height:24px">
304
- <span style="font-weight:bold">${v.typeDisplay}</span>
305
- ${v.finalKeyValue
287
+ <div class="section-padding">
288
+ <div class='sub-title regular-font'>AUTHENTICATION</div>
289
+ <div class="small-font-size" style="display:flex; align-items: center; min-height:30px">
290
+ ${providedApiKeys.length > 0
291
+ ? html`
292
+ <div class="blue-text"> ${providedApiKeys.length} API key applied </div>
293
+ <div style="flex:1"></div>
294
+ <button class="m-btn thin-border" part="btn btn-outline" @click=${() => { onClearAllApiKeys.call(this); }}>CLEAR ALL API KEYS</button>`
295
+ : html`<div class="red-text">No API key applied</div>`
296
+ }
297
+ </div>
298
+ ${schemes.length > 0
299
+ ? html`
300
+ <table class='m-table' style = "width:100%">
301
+ ${schemes.map((v) => html`
302
+ <tr>
303
+ <td style="max-width:500px; overflow-wrap: break-word;">
304
+ <div style="min-height:24px">
305
+ <span style="font-weight:bold">${v.typeDisplay}</span>
306
+ ${v.finalKeyValue
307
+ ? html`
308
+ <span class='blue-text'> ${v.finalKeyValue ? 'Key Applied' : ''} </span>
309
+ <button class="m-btn thin-border small" part="btn btn-outline" @click=${() => { v.finalKeyValue = ''; this.requestUpdate(); }}>REMOVE</button>
310
+ `
311
+ : ''
312
+ }
313
+ </div>
314
+ ${v.description
306
315
  ? html`
307
- <span class='blue-text'> ${v.finalKeyValue ? 'Key Applied' : ''} </span>
308
- <button class="m-btn thin-border small" part="btn btn-outline" @click=${() => { v.finalKeyValue = ''; this.requestUpdate(); }}>REMOVE</button>
309
- `
316
+ <div class="m-markdown">
317
+ ${unsafeHTML(marked(v.description || ''))}
318
+ </div>`
310
319
  : ''
311
320
  }
312
- </div>
313
- ${v.description
314
- ? html`
315
- <div class="m-markdown">
316
- ${unsafeHTML(marked(v.description || ''))}
317
- </div>`
318
- : ''
319
- }
320
- </td>
321
- <td>
322
- ${v.type && (v.type.toLowerCase() === 'apikey' || v.type.toLowerCase() === 'http' && v.scheme && v.scheme.toLowerCase() === 'bearer')
323
- ? html`
324
- ${v.type.toLowerCase() === 'apikey'
325
- ? html`Send <code>${v.name}</code> in <code>${v.in}</code> with the given value:`
326
- : html`Send <code>Authorization</code> in <code>header</code> containing the word <code>Bearer</code> followed by a space and a Token String.`
327
- }
328
- <div style="display:flex;">
329
- ${v.in === 'cookie'
330
- ? html`
331
- <div style="display: block">
332
- <input type="text" value="${getCookieValue(v.apiKeyId)}" disabled class="api-key-input" placeholder="IygRVGf54B59e0GAkKmigGfuiVlp/uhFfk2ifA+jMMJzau2F1jPldc09gPTfnMw13BFBxqUZIFDm55DPfwkb0A==" spellcheck = "false" style="resize: horizontal; width: 100%">
333
- <br>
334
- <small>
335
- <strong>Cookies</strong>&nbsp;are set and configured by the remote service, therefore it is not possible to configure them from the browser.
336
- </small>
337
- </div>`
338
- : html`
339
- <input type = "text" value = "${v.value}" class="api-key-input" placeholder = "api-token" spellcheck = "false">
340
- <button class="m-btn thin-border" style = "margin-left:5px;"
341
- part = "btn btn-outline"
342
- @click="${(e) => { onApiKeyChange.call(this, v.apiKeyId, e); }}">
343
- ${v.finalKeyValue ? 'UPDATE' : 'SET'}
344
- </button>`
321
+ </td>
322
+ <td>
323
+ ${v.type && (v.type.toLowerCase() === 'apikey' || v.type.toLowerCase() === 'http' && v.scheme && v.scheme.toLowerCase() === 'bearer')
324
+ ? html`
325
+ ${v.type.toLowerCase() === 'apikey'
326
+ ? html`Send <code>${v.name}</code> in <code>${v.in}</code> with the given value:`
327
+ : html`Send <code>Authorization</code> in <code>header</code> containing the word <code>Bearer</code> followed by a space and a Token String.`
345
328
  }
346
- </div>`
347
- : ''
348
- }
349
- ${v.type && v.type.toLowerCase() === 'http' && v.scheme && v.scheme.toLowerCase() === 'basic'
350
- ? html`
351
- Send the <code>Authorization</code> header containing the type <code>Basic</code> followed by a space and a base64 encoded string of <code>username:password</code>.
352
- <div style="display:flex;">
353
- <input type="text" value = "${v.user}" placeholder="username" spellcheck="false" class="api-key-user" style="width:100px">
354
- <input type="password" value = "${v.password}" placeholder="password" spellcheck="false" class="api-key-password" style = "width:100px; margin:0 5px;">
355
- <button class="m-btn thin-border"
356
- @click="${(e) => { onApiKeyChange.call(this, v.apiKeyId, e); }}"
357
- part = "btn btn-outline"
358
- >
359
- ${v.finalKeyValue ? 'UPDATE' : 'SET'}
360
- </button>
361
- </div>`
362
- : ''
363
- }
364
- </td>
365
- </tr>
366
- ${v.type.toLowerCase() === 'oauth2'
367
- ? html`
368
- <tr>
369
- <td colspan="2" style="border:none; padding-left:48px">
370
- ${Object.keys(v.flows).map((f) => oAuthFlowTemplate.call(
371
- this, f, v['x-client-id'], v['x-client-secret'], v.apiKeyId, v.flows[f],
372
- ))}
373
- </td>
374
- </tr>
375
- `
376
- : ''
377
- }
378
- `)}
379
- </table>`
380
- : ''
381
- }
329
+ <div style="display:flex;">
330
+ ${v.in === 'cookie'
331
+ ? html`
332
+ <div style="display: block">
333
+ <input type="text" value="${getCookieValue(v.apiKeyId)}" disabled class="api-key-input" placeholder="IygRVGf54B59e0GAkKmigGfuiVlp/uhFfk2ifA+jMMJzau2F1jPldc09gPTfnMw13BFBxqUZIFDm55DPfwkb0A==" spellcheck = "false" style="resize: horizontal; width: 100%">
334
+ <br>
335
+ <small>
336
+ <strong>Cookies</strong>&nbsp;are set and configured by the remote service, therefore it is not possible to configure them from the browser.
337
+ </small>
338
+ </div>`
339
+ : html`
340
+ <input type = "text" value = "${v.value}" class="api-key-input" placeholder = "api-token" spellcheck = "false">
341
+ <button class="m-btn thin-border" style = "margin-left:5px;"
342
+ part = "btn btn-outline"
343
+ @click="${(e) => { onApiKeyChange.call(this, v.apiKeyId, e); }}">
344
+ ${v.finalKeyValue ? 'UPDATE' : 'SET'}
345
+ </button>`
346
+ }
347
+ </div>`
348
+ : ''
349
+ }
350
+ ${v.type && v.type.toLowerCase() === 'http' && v.scheme && v.scheme.toLowerCase() === 'basic'
351
+ ? html`
352
+ Send the <code>Authorization</code> header containing the type <code>Basic</code> followed by a space and a base64 encoded string of <code>username:password</code>.
353
+ <div style="display:flex;">
354
+ <input type="text" value = "${v.user}" placeholder="username" spellcheck="false" class="api-key-user" style="width:100px">
355
+ <input type="password" value = "${v.password}" placeholder="password" spellcheck="false" class="api-key-password" style = "width:100px; margin:0 5px;">
356
+ <button class="m-btn thin-border"
357
+ @click="${(e) => { onApiKeyChange.call(this, v.apiKeyId, e); }}"
358
+ part = "btn btn-outline"
359
+ >
360
+ ${v.finalKeyValue ? 'UPDATE' : 'SET'}
361
+ </button>
362
+ </div>`
363
+ : ''
364
+ }
365
+ </td>
366
+ </tr>
367
+ ${v.type.toLowerCase() === 'oauth2'
368
+ ? html`
369
+ <tr>
370
+ <td colspan="2" style="border:none; padding-left:48px">
371
+ ${Object.keys(v.flows).map((f) => oAuthFlowTemplate.call(
372
+ this, f, v['x-client-id'], v['x-client-secret'], v.apiKeyId, v.flows[f],
373
+ ))}
374
+ </td>
375
+ </tr>
376
+ `
377
+ : ''
378
+ }
379
+ `)}
380
+ </table>`
381
+ : ''
382
+ }
383
+ </div>
382
384
  </slot>
383
385
  </section>
384
386
  `;
@@ -77,7 +77,7 @@ export default function serverTemplate() {
77
77
  return undefined;
78
78
  }
79
79
  return html`
80
- <section id = 'servers' part="section-servers" style="margin-top:24px; margin-bottom:24px;" class='regular-font observe-me ${this.renderStyle === 'read' ? 'section-gap--read-mode' : (this.renderStyle === 'focused' ? 'section-gap--focused-mode' : 'section-gap')}'>
80
+ <section id = 'servers' part="section-servers" style="margin-top:24px; margin-bottom:24px;" class='regular-font observe-me section-padding ${this.renderStyle === 'read' ? 'section-gap--read-mode' : (this.renderStyle === 'focused' ? 'section-gap--focused-mode' : 'section-gap')}'>
81
81
  <div class = 'sub-title'>API SERVER</div>
82
82
  <div class = 'mono-font' style='margin: 12px 0; font-size:calc(var(--font-size-small) + 1px);'>
83
83
  ${!this.resolvedSpec.servers || !this.resolvedSpec.servers.length
@@ -136,7 +136,7 @@ export function getCurrentElement() {
136
136
  }
137
137
 
138
138
  export function replaceState(rawElementId) {
139
- const elementId = rawElementId.replace(/^#/, '');
139
+ const elementId = rawElementId && rawElementId.replace(/^#/, '') || '';
140
140
 
141
141
  const currentNavigationHashPart = (window.location.hash || '').split('?')[0].replace(/^#/, '');
142
142
  const currentQuery = (window.location.hash || '').split('?')[1];
@@ -631,7 +631,8 @@ export function schemaInObjectNotation(schema, obj, level = 0, suffix = '') {
631
631
  }
632
632
 
633
633
  /* Create Example object */
634
- export function generateExample(examples, example, schema, mimeType, includeReadOnly = true, includeWriteOnly = true, outputType) {
634
+ export function generateExample(examples, example, schema, rawMimeType, includeReadOnly = true, includeWriteOnly = true, outputType) {
635
+ const mimeType = rawMimeType || 'application/json';
635
636
  const finalExamples = [];
636
637
  // First check if examples is provided
637
638
  if (examples) {
@@ -281,6 +281,18 @@ function groupByTags(openApiSpec, generateMissingTags = false) {
281
281
  finalParameters = pathOrHookObj.parameters ? pathOrHookObj.parameters.slice(0) : [];
282
282
  }
283
283
 
284
+ // Remove bad callbacks
285
+ if (pathOrHookObj.callbacks) {
286
+ for (const [callbackName, callbackConfig] of Object.entries(pathOrHookObj.callbacks)) {
287
+ const originalCallbackEntries = Object.entries(callbackConfig);
288
+ const filteredCallbacks = originalCallbackEntries.filter((entry) => typeof entry[1] === 'object') || [];
289
+ pathOrHookObj.callbacks[callbackName] = Object.fromEntries(filteredCallbacks);
290
+ if (filteredCallbacks.length !== originalCallbackEntries.length) {
291
+ console.warn(`OpenAPI Explorer: Invalid Callback found in ${callbackName}`); // eslint-disable-line no-console
292
+ }
293
+ }
294
+ }
295
+
284
296
  // Update Responses
285
297
  tagObj.paths.push({
286
298
  show: true,