rapidoc 9.3.6 → 9.3.8

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 (44) hide show
  1. package/.eslintrc +9 -1
  2. package/dist/index.html +1010 -6
  3. package/dist/index.html.gz +0 -0
  4. package/dist/rapidoc-min.js +444 -428
  5. package/dist/rapidoc-min.js.LICENSE.txt +1 -8
  6. package/dist/rapidoc-min.js.LICENSE.txt.gz +0 -0
  7. package/dist/rapidoc-min.js.gz +0 -0
  8. package/dist/rapidoc-min.js.map +1 -1
  9. package/dist/rapidoc-min.js.map.gz +0 -0
  10. package/dist/rapidoc.js +576 -373
  11. package/dist/report.html +2 -2
  12. package/index.html +1006 -209
  13. package/{index1.html → index_bak.html} +13 -6
  14. package/jsconfig.json +1 -1
  15. package/package.json +8 -8
  16. package/src/components/api-request.js +144 -100
  17. package/src/components/api-response.js +12 -13
  18. package/src/components/json-tree.js +1 -1
  19. package/src/components/schema-table.js +16 -6
  20. package/src/components/schema-tree.js +15 -10
  21. package/src/json-schema-viewer.js +7 -2
  22. package/src/rapidoc-mini.js +22 -4
  23. package/src/rapidoc.js +35 -19
  24. package/src/styles/border-styles.js +2 -2
  25. package/src/styles/endpoint-styles.js +13 -10
  26. package/src/styles/flex-styles.js +1 -1
  27. package/src/styles/font-styles.js +13 -13
  28. package/src/styles/input-styles.js +8 -3
  29. package/src/styles/nav-styles.js +1 -1
  30. package/src/styles/schema-styles.js +12 -9
  31. package/src/templates/advance-search-template.js +2 -1
  32. package/src/templates/callback-template.js +3 -1
  33. package/src/templates/code-samples-template.js +3 -3
  34. package/src/templates/endpoint-template.js +33 -28
  35. package/src/templates/expanded-endpoint-template.js +7 -3
  36. package/src/templates/json-schema-viewer-template.js +3 -3
  37. package/src/templates/logo-template.js +1 -1
  38. package/src/templates/main-body-template.js +6 -17
  39. package/src/templates/navbar-template.js +6 -6
  40. package/src/templates/security-scheme-template.js +14 -10
  41. package/src/utils/common-utils.js +5 -9
  42. package/src/utils/schema-utils.js +65 -16
  43. package/src/utils/spec-parser.js +77 -7
  44. package/src/utils/theme.js +4 -6
@@ -5,6 +5,8 @@
5
5
  <meta charset="utf-8">
6
6
  <meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
7
7
  <title>RapiDoc</title>
8
+ <link rel="stylesheet" href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.18.1/build/styles/default.min.css">
9
+ <script src="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.18.1/build/highlight.min.js"></script>
8
10
  </head>
9
11
 
10
12
  <script>
@@ -26,27 +28,32 @@
26
28
  https://assets.zuora.com/zuora-documentation/swagger.yaml <<< Large spec with lot of markdown
27
29
  ./specs/petstore_anyof.yaml <<< contains complex one-of with inline primitives and recursive objects
28
30
  ./specs/oneof1.yaml <<< nested oneof
31
+ https://cesdev.eng.sonicwall.com/api/q/openapi
29
32
  -->
30
33
  <rapi-doc
31
- spec-url = "./specs/petstore.yaml"
32
- route-prefix="#tab-pets/"
34
+ spec-url = "./specs/temp.yaml"
33
35
  id="thedoc"
34
36
  theme = "dark"
35
- render-style="read"
37
+ render-style="focused"
36
38
  schema-style="tree"
37
- use-path-in-nav-bar="true"
39
+ show-method-in-nav-bar = "true"
40
+ use-path-in-nav-bar = "true"
38
41
  show-components = "true"
39
42
  show-info = "true"
40
43
  show-header = "true"
41
- allow-search = "false"
44
+ allow-search = "true"
42
45
  allow-advanced-search = "true"
43
46
  allow-spec-url-load="false"
44
- allow-spec-file-load="false"
45
47
  allow-spec-file-download="true"
46
48
  allow-server-selection = "true"
47
49
  allow-authentication = "true"
50
+ update-route="false"
51
+ remove-endpoints-with-badge-label-as="one"
48
52
  match-type="regex"
49
53
  persist-auth="true"
54
+ scroll-behavior="smooth"
55
+ allow-try = "false"
56
+ show-curl-before-try = "true"
50
57
  ></rapi-doc>
51
58
  <!--
52
59
  <rapi-doc-mini
package/jsconfig.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "baseUrl": ".",
6
6
  // Relative to "baseUrl"
7
7
  "paths": {
8
- "@/*": ["./src/*"],
8
+ "~/*": ["./src/*"],
9
9
  }
10
10
  }
11
11
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rapidoc",
3
- "version": "9.3.6",
3
+ "version": "9.3.8",
4
4
  "description": "RapiDoc - Open API spec viewer with built in console",
5
5
  "author": "Mrinmoy Majumdar <mrin9@yahoo.com>",
6
6
  "homepage": "https://rapidocweb.com",
@@ -33,10 +33,10 @@
33
33
  "module": "dist/rapidoc-min.js",
34
34
  "mode": "production",
35
35
  "dependencies": {
36
- "@apitools/openapi-parser": "0.0.32",
36
+ "@apitools/openapi-parser": "0.0.33",
37
37
  "base64-arraybuffer": "^1.0.2",
38
38
  "buffer": "^6.0.3",
39
- "lit": "^3.2.0",
39
+ "lit": "^3.2.1",
40
40
  "marked": "4.3.0",
41
41
  "prismjs": "^1.29.0",
42
42
  "randexp": "^0.5.3",
@@ -52,9 +52,9 @@
52
52
  "lint-fix": "./node_modules/eslint/bin/eslint.js --fix ./src/**"
53
53
  },
54
54
  "devDependencies": {
55
- "@babel/core": "^7.25.2",
56
- "@babel/eslint-parser": "^7.25.1",
57
- "@babel/preset-env": "^7.25.4",
55
+ "@babel/core": "^7.25.8",
56
+ "@babel/eslint-parser": "^7.25.8",
57
+ "@babel/preset-env": "^7.25.8",
58
58
  "babel-loader": "^9.2.1",
59
59
  "babel-plugin-template-html-minifier": "^4.1.0",
60
60
  "clean-webpack-plugin": "^4.0.0",
@@ -63,7 +63,7 @@
63
63
  "eslint": "^8.57.1",
64
64
  "eslint-config-airbnb": "^19.0.4",
65
65
  "eslint-import-resolver-webpack": "^0.13.9",
66
- "eslint-plugin-import": "^2.30.0",
66
+ "eslint-plugin-import": "^2.31.0",
67
67
  "eslint-webpack-plugin": "4.2.0",
68
68
  "file-loader": "^6.2.0",
69
69
  "filemanager-webpack-plugin": "^8.0.0",
@@ -72,7 +72,7 @@
72
72
  "style-loader": "^4.0.0",
73
73
  "terser-webpack-plugin": "^5.3.10",
74
74
  "unminified-webpack-plugin": "^3.0.0",
75
- "webpack": "^5.94.0",
75
+ "webpack": "^5.95.0",
76
76
  "webpack-bundle-analyzer": "^4.10.2",
77
77
  "webpack-cli": "^5.1.4",
78
78
  "webpack-dev-server": "4.11.1"
@@ -2,6 +2,7 @@ import { LitElement, html, css } from 'lit';
2
2
  import { unsafeHTML } from 'lit/directives/unsafe-html.js'; // eslint-disable-line import/extensions
3
3
  import { guard } from 'lit/directives/guard.js'; // eslint-disable-line import/extensions
4
4
  import { live } from 'lit/directives/live.js'; // eslint-disable-line import/extensions
5
+ import { ifDefined } from 'lit/directives/if-defined.js'; // eslint-disable-line import/extensions
5
6
  import { marked } from 'marked';
6
7
  import formatXml from 'xml-but-prettier';
7
8
  import Prism from 'prismjs';
@@ -20,7 +21,7 @@ import { schemaInObjectNotation,
20
21
  normalizeExamples,
21
22
  getSchemaFromParam,
22
23
  json2xml,
23
- nestExampleIfPresent,
24
+ standardizeExample,
24
25
  anyExampleWithSummaryOrDescription } from '~/utils/schema-utils';
25
26
  import '~/components/json-tree';
26
27
  import '~/components/schema-tree';
@@ -99,6 +100,9 @@ export default class ApiRequest extends LitElement {
99
100
  TabStyles,
100
101
  PrismStyles,
101
102
  css`
103
+ :host {
104
+ container-type: inline-size;
105
+ }
102
106
  *, *:before, *:after { box-sizing: border-box; }
103
107
  :where(button, input[type="checkbox"], [tabindex="0"]):focus-visible { box-shadow: var(--focus-shadow); }
104
108
  :where(input[type="text"], input[type="password"], select, textarea):focus-visible { border-color: var(--primary-color); }
@@ -183,13 +187,13 @@ export default class ApiRequest extends LitElement {
183
187
  opacity: 1;
184
188
  }
185
189
 
186
- @media only screen and (min-width: 768px) {
190
+ @container (min-width: 768px) {
187
191
  .textarea {
188
192
  padding:8px;
189
193
  }
190
194
  }
191
195
 
192
- @media only screen and (max-width: 470px) {
196
+ @container (max-width: 470px) {
193
197
  .hide-in-small-screen {
194
198
  display:none;
195
199
  }
@@ -199,6 +203,7 @@ export default class ApiRequest extends LitElement {
199
203
  ];
200
204
  }
201
205
 
206
+ /* eslint-disable indent */
202
207
  render() {
203
208
  return html`
204
209
  <div class="col regular-font request-panel ${'read focused'.includes(this.renderStyle) || this.callback === 'true' ? 'read-mode' : 'view-mode'}">
@@ -270,7 +275,6 @@ export default class ApiRequest extends LitElement {
270
275
  }
271
276
  }
272
277
 
273
- /* eslint-disable indent */
274
278
  renderExample(example, paramType, paramName) {
275
279
  return html`
276
280
  ${paramType === 'array' ? '[' : ''}
@@ -353,7 +357,7 @@ export default class ApiRequest extends LitElement {
353
357
  let paramStyle = 'form';
354
358
  let paramExplode = true;
355
359
  let paramAllowReserved = false;
356
- if (paramType === 'query') {
360
+ if (paramType === 'query' || paramType === 'header' || paramType === 'path') {
357
361
  if (param.style && 'form spaceDelimited pipeDelimited'.includes(param.style)) {
358
362
  paramStyle = param.style;
359
363
  } else if (serializeStyle) {
@@ -368,12 +372,12 @@ export default class ApiRequest extends LitElement {
368
372
  }
369
373
  // openapi 3.1.0 spec based examples (which must be Object(string : { value:any, summary?: string, description?: string})
370
374
  const example = normalizeExamples(
371
- (param.examples
372
- || nestExampleIfPresent(param.example)
373
- || nestExampleIfPresent(mimeTypeElem?.example)
374
- || mimeTypeElem?.examples
375
- || nestExampleIfPresent(paramSchema.examples)
376
- || nestExampleIfPresent(paramSchema.example)
375
+ (standardizeExample(param.examples)
376
+ || standardizeExample(param.example)
377
+ || standardizeExample(mimeTypeElem?.example)
378
+ || standardizeExample(mimeTypeElem?.examples)
379
+ || standardizeExample(paramSchema.examples)
380
+ || standardizeExample(paramSchema.example)
377
381
  ),
378
382
  paramSchema.type,
379
383
  );
@@ -381,8 +385,8 @@ export default class ApiRequest extends LitElement {
381
385
  example.exampleVal = generateExample(
382
386
  declaredParamSchema,
383
387
  serializeStyle || 'json',
384
- '',
385
- '',
388
+ {},
389
+ {},
386
390
  this.callback === 'true' || this.webhook === 'true' ? true : false, // eslint-disable-line no-unneeded-ternary
387
391
  this.callback === 'true' || this.webhook === 'true' ? false : true, // eslint-disable-line no-unneeded-ternary
388
392
  true,
@@ -395,7 +399,10 @@ export default class ApiRequest extends LitElement {
395
399
  <tr title="${param.deprecated ? 'Deprecated' : ''}">
396
400
  <td rowspan="${this.allowTry === 'true' ? '1' : '2'}" style="width:${labelColWidth}; min-width:100px;">
397
401
  <div class="param-name ${param.deprecated ? 'deprecated' : ''}" >
398
- ${param.deprecated ? html`<span style='color:var(--red);'>✗</span>` : ''}
402
+ ${param.deprecated
403
+ ? html`<svg viewBox="0 0 10 10" width="10" height="10" style="stroke:var(--red); margin-right:-6px"><path d="M2 2L8 8M2 8L8 2"/></svg>`
404
+ : ''
405
+ }
399
406
  ${param.required ? html`<span style='color:var(--red)'>*</span>` : ''}
400
407
  ${param.name}
401
408
  </div>
@@ -412,6 +419,7 @@ export default class ApiRequest extends LitElement {
412
419
  ${paramSchema.type === 'array'
413
420
  ? html`
414
421
  <tag-input class="request-param"
422
+ id = "tag-input-request-param-${param.name}"
415
423
  style = "width:100%"
416
424
  data-ptype = "${paramType}"
417
425
  data-pname = "${param.name}"
@@ -430,58 +438,60 @@ export default class ApiRequest extends LitElement {
430
438
  </tag-input>`
431
439
  : paramSchema.type === 'object'
432
440
  ? html`
433
- <div class="tab-panel col" style="border-width:0 0 1px 0;">
434
- <div class="tab-buttons row" @click="${(e) => {
441
+ <div part="tab-panel" class="tab-panel col" style="border-width:0 0 1px 0;">
442
+ <div part="tab-btn-row" class="tab-buttons row" @click="${(e) => {
435
443
  if (e.target.tagName.toLowerCase() === 'button') {
436
444
  const newState = { ...this.activeParameterSchemaTabs };
437
445
  newState[param.name] = e.target.dataset.tab;
438
446
  this.activeParameterSchemaTabs = newState;
439
447
  }
440
448
  }}">
441
- <button class="tab-btn ${this.activeParameterSchemaTabs[param.name] === 'example' ? 'active' : ''}" data-tab = 'example'>EXAMPLE </button>
442
- <button class="tab-btn ${this.activeParameterSchemaTabs[param.name] !== 'example' ? 'active' : ''}" data-tab = 'schema'>SCHEMA</button>
449
+ <button part="tab-btn" class="tab-btn ${this.activeParameterSchemaTabs[param.name] === 'example' ? 'active' : ''}" data-tab = 'example'>EXAMPLE </button>
450
+ <button part="tab-btn" class="tab-btn ${this.activeParameterSchemaTabs[param.name] !== 'example' ? 'active' : ''}" data-tab = 'schema'>SCHEMA</button>
443
451
  </div>
444
- ${this.activeParameterSchemaTabs[param.name] === 'example'
445
- ? html`<div class="tab-content col">
446
- <textarea
447
- class = "textarea request-param"
448
- part = "textarea textarea-param"
449
- data-ptype = "${paramType}-object"
450
- data-pname = "${param.name}"
451
- data-example = "${example.exampleVal}"
452
- data-param-serialize-style = "${paramStyle}"
453
- data-param-serialize-explode = "${paramExplode}"
454
- data-param-allow-reserved = "${paramAllowReserved}"
455
- data-x-fill-example = "${param['x-fill-example'] || 'yes'}"
456
- spellcheck = "false"
457
- .textContent="${param['x-fill-example'] === 'no' ? '' : live(this.fillRequestFieldsWithExample === 'true' ? (typeof example.exampleVal === 'object' ? JSON.stringify(example.exampleVal, null, 2) : example.exampleVal) : '')}"
458
- style = "resize:vertical; width:100%; height: ${'read focused'.includes(this.renderStyle) ? '180px' : '120px'};"
459
- @input=${(e) => {
460
- const requestPanelEl = this.getRequestPanel(e);
461
- this.liveCURLSyntaxUpdate(requestPanelEl);
462
- }}
463
- ></textarea>
464
- </div>`
465
- : html`
466
- <div class="tab-content col">
467
- <schema-tree
468
- class = 'json'
469
- style = 'display: block'
470
- .data = '${schemaAsObj}'
471
- schema-expand-level = "${this.schemaExpandLevel}"
472
- schema-description-expanded = "${this.schemaDescriptionExpanded}"
473
- allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}"
474
- schema-hide-read-only = "${this.schemaHideReadOnly.includes(this.method)}"
475
- schema-hide-write-only = "${this.schemaHideWriteOnly.includes(this.method)}"
476
- exportparts = "wrap-request-btn:wrap-request-btn, btn:btn, btn-fill:btn-fill, btn-outline:btn-outline, btn-try:btn-try, btn-clear:btn-clear, btn-clear-resp:btn-clear-resp,
477
- file-input:file-input, textbox:textbox, textbox-param:textbox-param, textarea:textarea, textarea-param:textarea-param,
478
- anchor:anchor, anchor-param-example:anchor-param-example"
479
- > </schema-tree>
480
- </div>`
481
- }
482
- </div>`
452
+
453
+ ${html`<div part="tab-content" class="tab-content col" data-tab = 'example' style="display:${this.activeParameterSchemaTabs[param.name] === 'example' ? 'block' : 'none'}; padding-left:5px; width:100%">
454
+ <textarea
455
+ id = "textarea-request-param-${param.name}"
456
+ class = "textarea request-param"
457
+ part = "textarea textarea-param"
458
+ data-ptype = "${paramType}-object"
459
+ data-pname = "${param.name}"
460
+ data-example = "${example.exampleVal}"
461
+ data-param-serialize-style = "${paramStyle}"
462
+ data-param-serialize-explode = "${paramExplode}"
463
+ data-param-allow-reserved = "${paramAllowReserved}"
464
+ data-x-fill-example = "${param['x-fill-example'] || 'yes'}"
465
+ spellcheck = "false"
466
+ .textContent="${param['x-fill-example'] === 'no' ? '' : live(this.fillRequestFieldsWithExample === 'true' ? (typeof example.exampleVal === 'object' ? JSON.stringify(example.exampleVal, null, 2) : example.exampleVal) : '')}"
467
+ style = "resize:vertical; width:100%; height: ${'read focused'.includes(this.renderStyle) ? '180px' : '120px'};"
468
+ @input=${(e) => {
469
+ const requestPanelEl = this.getRequestPanel(e);
470
+ this.liveCURLSyntaxUpdate(requestPanelEl);
471
+ }}
472
+ ></textarea>
473
+ </div>`
474
+ }
475
+ ${html`<div part="tab-content" class="tab-content col" data-tab = 'schema' style="display:${this.activeParameterSchemaTabs[param.name] !== 'example' ? 'block' : 'none'}; padding-left:5px; width:100%;">
476
+ <schema-tree
477
+ class = 'json'
478
+ style = 'display: block'
479
+ .data = '${schemaAsObj}'
480
+ schema-expand-level = "${this.schemaExpandLevel}"
481
+ schema-description-expanded = "${this.schemaDescriptionExpanded}"
482
+ allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}"
483
+ schema-hide-read-only = "${this.schemaHideReadOnly.includes(this.method)}"
484
+ schema-hide-write-only = "${this.schemaHideWriteOnly.includes(this.method)}"
485
+ exportparts = "wrap-request-btn:wrap-request-btn, btn:btn, btn-fill:btn-fill, btn-outline:btn-outline, btn-try:btn-try, btn-clear:btn-clear, btn-clear-resp:btn-clear-resp,
486
+ file-input:file-input, textbox:textbox, textbox-param:textbox-param, textarea:textarea, textarea-param:textarea-param,
487
+ anchor:anchor, anchor-param-example:anchor-param-example"
488
+ > </schema-tree>
489
+ </div>`
490
+ }
491
+ </div>`
483
492
  : html`
484
493
  <input type="${paramSchema.format === 'password' ? 'password' : 'text'}" spellcheck="false" style="width:100%"
494
+ id="input-request-param-${param.name}"
485
495
  class="request-param"
486
496
  part="textbox textbox-param"
487
497
  data-ptype="${paramType}"
@@ -641,8 +651,8 @@ export default class ApiRequest extends LitElement {
641
651
  reqBodyExamples = generateExample(
642
652
  reqBody.schema,
643
653
  reqBody.mimeType,
644
- reqBody.examples,
645
- reqBody.example,
654
+ standardizeExample(reqBody.examples),
655
+ standardizeExample(reqBody.example),
646
656
  this.callback === 'true' || this.webhook === 'true' ? true : false, // eslint-disable-line no-unneeded-ternary
647
657
  this.callback === 'true' || this.webhook === 'true' ? false : true, // eslint-disable-line no-unneeded-ternary
648
658
  'text',
@@ -725,7 +735,7 @@ export default class ApiRequest extends LitElement {
725
735
  if (reqBody.mimeType === this.selectedRequestBodyType) {
726
736
  reqBodyFileInputHtml = html`
727
737
  <div class = "small-font-size bold-text row">
728
- <input type="file" part="file-input" style="max-width:100%" class="request-body-param-file" data-ptype="${reqBody.mimeType}" spellcheck="false" />
738
+ <input id="input-request-body-param-file" type="file" part="file-input" style="max-width:100%" class="request-body-param-file" data-ptype="${reqBody.mimeType}" spellcheck="false" />
729
739
  </div>
730
740
  `;
731
741
  }
@@ -780,13 +790,13 @@ export default class ApiRequest extends LitElement {
780
790
 
781
791
  ${(this.selectedRequestBodyType.includes('json') || this.selectedRequestBodyType.includes('xml') || this.selectedRequestBodyType.includes('text') || this.selectedRequestBodyType.includes('jose'))
782
792
  ? html`
783
- <div class="tab-panel col" style="border-width:0 0 1px 0;">
784
- <div class="tab-buttons row" @click="${(e) => { if (e.target.tagName.toLowerCase() === 'button') { this.activeSchemaTab = e.target.dataset.tab; } }}">
785
- <button class="tab-btn ${this.activeSchemaTab === 'example' ? 'active' : ''}" data-tab = 'example'>EXAMPLE</button>
786
- <button class="tab-btn ${this.activeSchemaTab !== 'example' ? 'active' : ''}" data-tab = 'schema'>SCHEMA</button>
793
+ <div part="tab-panel" class="tab-panel col" style="border-width:0 0 1px 0;">
794
+ <div part="tab-btn-row" class="tab-buttons row" @click="${(e) => { if (e.target.tagName.toLowerCase() === 'button') { this.activeSchemaTab = e.target.dataset.tab; } }}">
795
+ <button part="tab-btn" class="tab-btn ${this.activeSchemaTab === 'example' ? 'active' : ''}" data-tab = 'example'>EXAMPLE</button>
796
+ <button part="tab-btn" class="tab-btn ${this.activeSchemaTab !== 'example' ? 'active' : ''}" data-tab = 'schema'>SCHEMA</button>
787
797
  </div>
788
- ${html`<div class="tab-content col" style="display:${this.activeSchemaTab === 'example' ? 'block' : 'none'};"> ${reqBodyExampleHtml}</div>`}
789
- ${html`<div class="tab-content col" style="display:${this.activeSchemaTab === 'example' ? 'none' : 'block'};"> ${reqBodySchemaHtml}</div>`}
798
+ ${html`<div part="tab-content" class="tab-content col" style="display:${this.activeSchemaTab === 'example' ? 'block' : 'none'};"> ${reqBodyExampleHtml}</div>`}
799
+ ${html`<div part="tab-content" class="tab-content col" style="display:${this.activeSchemaTab === 'example' ? 'none' : 'block'};"> ${reqBodySchemaHtml}</div>`}
790
800
  </div>`
791
801
  : html`
792
802
  ${reqBodyFileInputHtml}
@@ -802,8 +812,8 @@ export default class ApiRequest extends LitElement {
802
812
  const formdataPartExample = generateExample(
803
813
  fieldSchema,
804
814
  'json',
805
- fieldSchema.examples,
806
- fieldSchema.example,
815
+ standardizeExample(fieldSchema.examples),
816
+ standardizeExample(fieldSchema.example),
807
817
  this.callback === 'true' || this.webhook === 'true' ? true : false, // eslint-disable-line no-unneeded-ternary
808
818
  this.callback === 'true' || this.webhook === 'true' ? false : true, // eslint-disable-line no-unneeded-ternary
809
819
  'text',
@@ -811,7 +821,7 @@ export default class ApiRequest extends LitElement {
811
821
  );
812
822
 
813
823
  return html`
814
- <div class="tab-panel row" style="min-height:220px; border-left: 6px solid var(--light-border-color); align-items: stretch;">
824
+ <div part="tab-panel" class="tab-panel row" style="min-height:220px; border-left: 6px solid var(--light-border-color); align-items: stretch;">
815
825
  <div style="width:24px; background-color:var(--light-border-color)">
816
826
  <div class="row" style="flex-direction:row-reverse; width:160px; height:24px; transform:rotate(270deg) translateX(-160px); transform-origin:top left; display:block;" @click="${(e) => {
817
827
  if (e.target.classList.contains('v-tab-btn')) {
@@ -852,7 +862,7 @@ export default class ApiRequest extends LitElement {
852
862
  ${html`
853
863
  <div class="tab-content col" data-tab = 'schema' style="display:${this.activeSchemaTab !== 'example' ? 'block' : 'none'}; padding-left:5px; width:100%;">
854
864
  <schema-tree
855
- .data = '${formdataPartSchema}'
865
+ .data = "${formdataPartSchema}"
856
866
  schema-expand-level = "${this.schemaExpandLevel}"
857
867
  schema-description-expanded = "${this.schemaDescriptionExpanded}"
858
868
  allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}",
@@ -1014,7 +1024,7 @@ export default class ApiRequest extends LitElement {
1014
1024
  curlSyntaxTemplate(display = 'flex') {
1015
1025
  return html`
1016
1026
  <div class="col m-markdown" style="flex:1; display:${display}; position:relative; max-width: 100%;">
1017
- <button class="toolbar-btn" style = "position:absolute; top:12px; right:8px" @click='${(e) => { copyToClipboard(this.curlSyntax.replace(/\\$/, ''), e); }}' part="btn btn-fill"> Copy </button>
1027
+ <button class="toolbar-btn" style = "position:absolute; top:12px; right:8px" @click='${(e) => { copyToClipboard(this.curlSyntax.trim().replace(/\\$/, ''), e); }}' part="btn btn-fill"> Copy </button>
1018
1028
  <pre style="white-space:pre"><code>${unsafeHTML(Prism.highlight(this.curlSyntax.trim().replace(/\\$/, ''), Prism.languages.shell, 'shell'))}</code></pre>
1019
1029
  </div>
1020
1030
  `;
@@ -1045,22 +1055,22 @@ export default class ApiRequest extends LitElement {
1045
1055
  <div style="flex:1"></div>
1046
1056
  <button class="m-btn" part="btn btn-outline btn-clear-response" @click="${this.clearResponseData}">CLEAR RESPONSE</button>
1047
1057
  </div>
1048
- <div class="tab-panel col" style="border-width:0 0 1px 0;">
1049
- <div id="tab_buttons" class="tab-buttons row" @click="${(e) => {
1058
+ <div part="tab-panel" class="tab-panel col" style="border-width:0 0 1px 0;">
1059
+ <div id="tab_buttons" part="tab-btn-row" class="tab-buttons row" @click="${(e) => {
1050
1060
  if (e.target.classList.contains('tab-btn') === false) { return; }
1051
1061
  this.activeResponseTab = e.target.dataset.tab;
1052
1062
  }}">
1053
- <button class="tab-btn ${this.activeResponseTab === 'response' ? 'active' : ''}" data-tab = 'response' > RESPONSE</button>
1054
- <button class="tab-btn ${this.activeResponseTab === 'headers' ? 'active' : ''}" data-tab = 'headers' > RESPONSE HEADERS</button>
1063
+ <button part="tab-btn" class="tab-btn ${this.activeResponseTab === 'response' ? 'active' : ''}" data-tab = 'response' > RESPONSE</button>
1064
+ <button part="tab-btn" class="tab-btn ${this.activeResponseTab === 'headers' ? 'active' : ''}" data-tab = 'headers' > RESPONSE HEADERS</button>
1055
1065
  ${this.showCurlBeforeTry === 'true'
1056
1066
  ? ''
1057
- : html`<button class="tab-btn ${this.activeResponseTab === 'curl' ? 'active' : ''}" data-tab = 'curl'>CURL</button>`}
1067
+ : html`<button part="tab-btn" class="tab-btn ${this.activeResponseTab === 'curl' ? 'active' : ''}" data-tab = 'curl'>CURL</button>`}
1058
1068
  </div>
1059
1069
  ${this.responseIsBlob
1060
1070
  ? html`
1061
- <div class="tab-content col" style="flex:1; display:${this.activeResponseTab === 'response' ? 'flex' : 'none'};">
1071
+ <div part="tab-content" class="tab-content col" style="flex:1; display:${this.activeResponseTab === 'response' ? 'flex' : 'none'};">
1062
1072
  ${this.responseBlobType === 'image'
1063
- ? html`<img style="max-height:var(--resp-area-height, 400px); object-fit:contain;" class="mar-top-8" src="${this.responseBlobUrl}"></img>`
1073
+ ? html`<img style="max-height:var(--resp-area-height, 400px); object-fit:contain;" class="mar-top-8" src="${ifDefined(this.responseBlobUrl)}"></img>`
1064
1074
  : ''
1065
1075
  }
1066
1076
  <button class="m-btn thin-border mar-top-8" style="width:135px" @click='${(e) => { downloadResource(this.responseBlobUrl, this.respContentDisposition, e); }}' part="btn btn-outline">
@@ -1072,12 +1082,12 @@ export default class ApiRequest extends LitElement {
1072
1082
  }
1073
1083
  </div>`
1074
1084
  : html`
1075
- <div class="tab-content col m-markdown" style="flex:1; display:${this.activeResponseTab === 'response' ? 'flex' : 'none'};" >
1085
+ <div part="tab-content" class="tab-content col m-markdown" style="flex:1; display:${this.activeResponseTab === 'response' ? 'flex' : 'none'};" >
1076
1086
  <button class="toolbar-btn" style="position:absolute; top:12px; right:8px" @click='${(e) => { copyToClipboard(this.responseText, e); }}' part="btn btn-fill"> Copy </button>
1077
1087
  <pre style="white-space:pre; min-height:50px; height:var(--resp-area-height, 400px); resize:vertical; overflow:auto">${responseContent}</pre>
1078
1088
  </div>`
1079
1089
  }
1080
- <div class="tab-content col m-markdown" style="flex:1; display:${this.activeResponseTab === 'headers' ? 'flex' : 'none'};" >
1090
+ <div part="tab-content" class="tab-content col m-markdown" style="flex:1; display:${this.activeResponseTab === 'headers' ? 'flex' : 'none'};" >
1081
1091
  <button class="toolbar-btn" style = "position:absolute; top:12px; right:8px" @click='${(e) => { copyToClipboard(this.responseHeaders, e); }}' part="btn btn-fill"> Copy </button>
1082
1092
  <pre style="white-space:pre"><code>${unsafeHTML(Prism.highlight(this.responseHeaders, Prism.languages.css, 'css'))}</code></pre>
1083
1093
  </div>
@@ -1086,18 +1096,8 @@ export default class ApiRequest extends LitElement {
1086
1096
  }
1087
1097
 
1088
1098
  apiCallTemplate() {
1089
- let selectServerDropdownHtml = '';
1090
-
1091
- if (this.servers && this.servers.length > 0) {
1092
- selectServerDropdownHtml = html`
1093
- <select style="min-width:100px;" @change='${(e) => { this.serverUrl = e.target.value; }}'>
1094
- ${this.servers.map((v) => html`<option value = "${v.url}"> ${v.url} - ${v.description} </option>`)}
1095
- </select>
1096
- `;
1097
- }
1098
1099
  const selectedServerHtml = html`
1099
1100
  <div style="display:flex; flex-direction:column;">
1100
- ${selectServerDropdownHtml}
1101
1101
  ${this.serverUrl
1102
1102
  ? html`
1103
1103
  <div style="display:flex; align-items:baseline;">
@@ -1127,7 +1127,7 @@ export default class ApiRequest extends LitElement {
1127
1127
  : `${this.api_keys.length} API keys applied`
1128
1128
  }
1129
1129
  </div>`
1130
- : html`<div class="gray-text">Required <span style="color:var(--red)">(None Applied)</span>`
1130
+ : html`<div class="gray-text">Required <span style="color:var(--red)">(None Applied)</span> </div>`
1131
1131
  }`
1132
1132
  : html`<span class="gray-text"> Not Required </span>`
1133
1133
  }
@@ -1263,7 +1263,7 @@ export default class ApiRequest extends LitElement {
1263
1263
  }
1264
1264
  }
1265
1265
  }
1266
- } catch (err) {
1266
+ } catch {
1267
1267
  console.error('RapiDoc: unable to parse %s into object', el.value); // eslint-disable-line no-console
1268
1268
  }
1269
1269
  if (queryParam.toString()) {
@@ -1301,7 +1301,7 @@ export default class ApiRequest extends LitElement {
1301
1301
 
1302
1302
  buildFetchHeaders(requestPanelEl) {
1303
1303
  const respEl = this.closest('.expanded-req-resp-container, .req-resp-container')?.getElementsByTagName('api-response')[0];
1304
- const headerParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='header']")];
1304
+ const headerParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='header'], [data-ptype='header-object']")];
1305
1305
  const requestBodyContainerEl = requestPanelEl.querySelector('.request-body-container');
1306
1306
  const acceptHeader = respEl?.selectedMimeType;
1307
1307
  const reqHeaders = new Headers();
@@ -1322,7 +1322,38 @@ export default class ApiRequest extends LitElement {
1322
1322
  // Add Header Params
1323
1323
  headerParamEls.map((el) => {
1324
1324
  if (el.value) {
1325
- reqHeaders.append(el.dataset.pname, el.value);
1325
+ if (el.dataset.ptype === 'header-object') {
1326
+ /* CONVERT
1327
+ a header value from below object style
1328
+ {
1329
+ "key1": "val1",
1330
+ "key2": {
1331
+ "key2_1": "val2_1",
1332
+ "key2_2": {
1333
+ "key2_2_1": "val2_2_1"
1334
+ }
1335
+ },
1336
+ "key3": "val3"
1337
+ };
1338
+
1339
+ TO >>>
1340
+ key1=val1, key2={"key2_1":"val2_1","key2_2":{"key2_2_1":"val2_2_1"}}, key3=val3
1341
+ */
1342
+ const headerObjVal = JSON.parse(el.value.replace(/\n/g, '').trim());
1343
+ const firstLevelKeySeparator = el.dataset.paramSerializeExplode === 'true' ? '=' : ',';
1344
+ const headerStrVal = Object.keys(headerObjVal)
1345
+ .map((key) => {
1346
+ const value = headerObjVal[key];
1347
+ if (typeof value === 'object') {
1348
+ return `${key}${firstLevelKeySeparator}${JSON.stringify(value)}`;
1349
+ }
1350
+ return `${key}${firstLevelKeySeparator}${value}`;
1351
+ })
1352
+ .join(',');
1353
+ reqHeaders.append(el.dataset.pname, headerStrVal);
1354
+ } else {
1355
+ reqHeaders.append(el.dataset.pname, el.value);
1356
+ }
1326
1357
  }
1327
1358
  });
1328
1359
 
@@ -1334,7 +1365,6 @@ export default class ApiRequest extends LitElement {
1334
1365
  reqHeaders.append('Content-Type', requestBodyType);
1335
1366
  }
1336
1367
  }
1337
-
1338
1368
  return reqHeaders;
1339
1369
  }
1340
1370
 
@@ -1497,11 +1527,12 @@ export default class ApiRequest extends LitElement {
1497
1527
  respHeadersObj[hdr] = hdrVal;
1498
1528
  this.responseHeaders = `${this.responseHeaders}${hdr}: ${hdrVal}\n`;
1499
1529
  });
1500
- const contentType = fetchResponse.headers.get('content-type').split(';')[0].trim();
1530
+ let contentType = fetchResponse.headers.get('content-type');
1501
1531
  const respEmpty = (await fetchResponse.clone().text()).length === 0;
1502
1532
  if (respEmpty) {
1503
1533
  this.responseText = '';
1504
1534
  } else if (contentType) {
1535
+ contentType = contentType.split(';')[0].trim();
1505
1536
  if (contentType === 'application/x-ndjson') {
1506
1537
  this.responseText = await fetchResponse.text();
1507
1538
  } else if (contentType.includes('json')) {
@@ -1542,8 +1573,21 @@ export default class ApiRequest extends LitElement {
1542
1573
  }
1543
1574
  }
1544
1575
  if (this.responseIsBlob) {
1545
- const contentDisposition = fetchResponse.headers.get('content-disposition');
1546
- this.respContentDisposition = contentDisposition ? contentDisposition.split('filename=')[1].replace(/"|'/g, '') : 'filename';
1576
+ const contentDisposition = fetchResponse.headers.get('content-disposition') || '';
1577
+ let filenameFromContentDeposition = 'filename';
1578
+ if (contentDisposition) {
1579
+ const filenameStarRegexMatch = contentDisposition.match(/filename\*=\s*UTF-8''([^;]+)/); // Support Headers like >>> Content-Disposition: attachment; filename*=UTF-8''example%20file.pdf
1580
+ if (filenameStarRegexMatch) {
1581
+ filenameFromContentDeposition = decodeURIComponent(filenameStarRegexMatch[1]); // the filename* format in the Content-Disposition header follows RFC 5987, which allows encoding non-ASCII characters using percent encoding. so example%20file.pdf becomes example file.pdf
1582
+ } else {
1583
+ // Fallback to the regular filename format
1584
+ const filenameMatch = contentDisposition.match(/filename="?([^"]+)"?/); // Content-Disposition: attachment; filename=example.pdf
1585
+ if (filenameMatch) {
1586
+ filenameFromContentDeposition = filenameMatch[1];
1587
+ }
1588
+ }
1589
+ }
1590
+ this.respContentDisposition = filenameFromContentDeposition;
1547
1591
  respBlob = await fetchResponse.blob();
1548
1592
  this.responseBlobUrl = URL.createObjectURL(respBlob);
1549
1593
  }
@@ -1635,7 +1679,7 @@ export default class ApiRequest extends LitElement {
1635
1679
  fetchHeaders.set(key, tempHeaderArray.join(', '));
1636
1680
  });
1637
1681
 
1638
- curlHeaders = Array.from(fetchHeaders).map(([key, value]) => ` -H "${key}: ${value}"`).join('\\\n');
1682
+ curlHeaders = Array.from(fetchHeaders).map(([key, value]) => ` -H '${key}: ${value}'`).join('\\\n');
1639
1683
  if (curlHeaders) {
1640
1684
  curlHeaders = `${curlHeaders} \\\n`;
1641
1685
  }
@@ -1667,7 +1711,7 @@ export default class ApiRequest extends LitElement {
1667
1711
  if (requestBodyType.includes('json')) {
1668
1712
  try {
1669
1713
  curlData = ` -d '${JSON.stringify(JSON.parse(exampleTextAreaEl.value))}' \\\n`;
1670
- } catch (err) {
1714
+ } catch {
1671
1715
  // Ignore.
1672
1716
  }
1673
1717
  }