rapidoc 9.3.6 → 9.3.7

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 (38) hide show
  1. package/.eslintrc +9 -1
  2. package/dist/index.html +1 -1
  3. package/dist/index.html.gz +0 -0
  4. package/dist/rapidoc-min.js +390 -376
  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 +436 -242
  11. package/dist/report.html +2 -2
  12. package/index.html +5 -4
  13. package/package.json +2 -2
  14. package/src/components/api-request.js +115 -77
  15. package/src/components/api-response.js +6 -7
  16. package/src/components/schema-table.js +6 -5
  17. package/src/components/schema-tree.js +9 -7
  18. package/src/json-schema-viewer.js +6 -2
  19. package/src/rapidoc-mini.js +20 -4
  20. package/src/rapidoc.js +33 -18
  21. package/src/styles/border-styles.js +2 -2
  22. package/src/styles/endpoint-styles.js +13 -10
  23. package/src/styles/flex-styles.js +1 -1
  24. package/src/styles/font-styles.js +13 -13
  25. package/src/styles/input-styles.js +2 -2
  26. package/src/styles/nav-styles.js +1 -1
  27. package/src/styles/schema-styles.js +12 -9
  28. package/src/templates/advance-search-template.js +2 -1
  29. package/src/templates/endpoint-template.js +29 -26
  30. package/src/templates/expanded-endpoint-template.js +4 -2
  31. package/src/templates/json-schema-viewer-template.js +3 -3
  32. package/src/templates/main-body-template.js +2 -2
  33. package/src/templates/navbar-template.js +6 -6
  34. package/src/templates/security-scheme-template.js +9 -7
  35. package/src/utils/common-utils.js +5 -9
  36. package/src/utils/schema-utils.js +65 -16
  37. package/src/utils/spec-parser.js +77 -7
  38. package/src/utils/theme.js +4 -6
@@ -6,7 +6,7 @@ import '~/components/api-response';
6
6
  import codeSamplesTemplate from '~/templates/code-samples-template';
7
7
  import callbackTemplate from '~/templates/callback-template';
8
8
  import { pathSecurityTemplate } from '~/templates/security-scheme-template';
9
- import { pathIsInSearch, rapidocApiKey } from '~/utils/common-utils';
9
+ import { getMatchedPaths, rapidocApiKey } from '~/utils/common-utils';
10
10
 
11
11
  function toggleExpand(path) {
12
12
  if (path.expanded) {
@@ -91,7 +91,7 @@ function endpointBodyTemplate(path) {
91
91
  <div part="section-endpoint-body-${path.expanded ? 'expanded' : 'collapsed'}" class='endpoint-body ${path.method} ${path.deprecated ? 'deprecated' : ''}'>
92
92
  <div class="summary">
93
93
  ${path.summary
94
- ? html`<div class="title" part="section-endpoint-body-title">${path.summary}<div>`
94
+ ? html`<div class="title" part="section-endpoint-body-title">${path.summary}</div>`
95
95
  : path.shortSummary !== path.description
96
96
  ? html`<div class="title" part="section-endpoint-body-title">${path.shortSummary}</div>`
97
97
  : ''
@@ -100,7 +100,9 @@ function endpointBodyTemplate(path) {
100
100
  ? html`
101
101
  <div style="display:flex; flex-wrap:wrap;font-size: var(--font-size-small);">
102
102
  ${path.xBadges.map((v) => (
103
- html`<span part="endpoint-badge" style="margin:1px; margin-right:5px; padding:1px 8px; font-weight:bold; border-radius:12px; background-color: var(--light-${v.color}, var(--input-bg)); color:var(--${v.color}); border:1px solid var(--${v.color})">${v.label}</span>`
103
+ v.color === 'none'
104
+ ? ''
105
+ : html`<span part="endpoint-badge" style="margin:1px; margin-right:5px; padding:1px 8px; font-weight:bold; border-radius:12px; background-color: var(--light-${v.color}, var(--input-bg)); color:var(--${v.color}); border:1px solid var(--${v.color})">${v.label}</span>`
104
106
  ))
105
107
  }
106
108
  </div>
@@ -138,7 +140,7 @@ function endpointBodyTemplate(path) {
138
140
  .request_body = "${path.requestBody}"
139
141
  .api_keys = "${nonEmptyApiKeys}"
140
142
  .servers = "${path.servers}"
141
- server-url = "${path.servers && path.servers.length > 0 ? path.servers[0].url : this.selectedServer.computedUrl}"
143
+ server-url = "${path.servers && path.servers.length > 0 ? path.servers[0].url : this.selectedServer?.computedUrl}"
142
144
  active-schema-tab = "${this.defaultSchemaTab}"
143
145
  fill-request-fields-with-example = "${this.fillRequestFieldsWithExample}"
144
146
  allow-try = "${this.allowTry}"
@@ -182,11 +184,12 @@ function endpointBodyTemplate(path) {
182
184
  </div>`;
183
185
  }
184
186
 
185
- export default function endpointTemplate(showExpandCollapse = true, showTags = true, pathsExpanded = false) {
187
+ export default function endpointTemplate(isMini = false, pathsExpanded = false) {
186
188
  if (!this.resolvedSpec) { return ''; }
187
189
  return html`
188
- ${showExpandCollapse
189
- ? html`
190
+ ${isMini
191
+ ? ''
192
+ : html`
190
193
  <div style="display:flex; justify-content:flex-end;">
191
194
  <span @click="${(e) => onExpandCollapseAll(e, 'expand-all')}" style="color:var(--primary-color); cursor:pointer;">
192
195
  Expand all
@@ -197,11 +200,25 @@ export default function endpointTemplate(showExpandCollapse = true, showTags = t
197
200
  </span>
198
201
  &nbsp; sections
199
202
  </div>`
200
- : ''
201
203
  }
202
204
  ${this.resolvedSpec.tags.map((tag) => html`
203
- ${showTags
204
- ? html`
205
+ ${isMini
206
+ ? html`
207
+ <div class='section-tag-body'>
208
+ ${tag.paths.filter((path) => {
209
+ if (this.searchVal) {
210
+ return getMatchedPaths(this.searchVal, path, tag.name);
211
+ }
212
+ return true;
213
+ }).map((path) => html`
214
+ <section id='${path.elementId}' class='m-endpoint regular-font ${path.method} ${pathsExpanded || path.expanded ? 'expanded' : 'collapsed'}'>
215
+ ${endpointHeadTemplate.call(this, path, pathsExpanded)}
216
+ ${pathsExpanded || path.expanded ? endpointBodyTemplate.call(this, path) : ''}
217
+ </section>`)
218
+ }
219
+ </div>
220
+ `
221
+ : html`
205
222
  <div class='regular-font section-gap section-tag ${tag.expanded ? 'expanded' : 'collapsed'}'>
206
223
  <div class='section-tag-header' @click="${() => { tag.expanded = !tag.expanded; this.requestUpdate(); }}">
207
224
  <div id='${tag.elementId}' class="sub-title tag" style="color:var(--primary-color)">${tag.displayName || tag.name}</div>
@@ -212,8 +229,8 @@ export default function endpointTemplate(showExpandCollapse = true, showTags = t
212
229
  ${unsafeHTML(marked(tag.description || ''))}
213
230
  </div>
214
231
  ${tag.paths.filter((v) => {
215
- if (this.matchPaths) {
216
- return pathIsInSearch(this.matchPaths, v, this.matchType);
232
+ if (this.searchVal) {
233
+ return getMatchedPaths(this.searchVal, v, tag.name);
217
234
  }
218
235
  return true;
219
236
  }).map((path) => html`
@@ -223,20 +240,6 @@ export default function endpointTemplate(showExpandCollapse = true, showTags = t
223
240
  </section>`)
224
241
  }
225
242
  </div>
226
- </div>`
227
- : html`
228
- <div class='section-tag-body'>
229
- ${tag.paths.filter((v) => {
230
- if (this.matchPaths) {
231
- return pathIsInSearch(this.matchPaths, v, this.matchType);
232
- }
233
- return true;
234
- }).map((path) => html`
235
- <section id='${path.elementId}' class='m-endpoint regular-font ${path.method} ${pathsExpanded || path.expanded ? 'expanded' : 'collapsed'}'>
236
- ${endpointHeadTemplate.call(this, path, pathsExpanded)}
237
- ${pathsExpanded || path.expanded ? endpointBodyTemplate.call(this, path) : ''}
238
- </section>`)
239
- }
240
243
  </div>
241
244
  `
242
245
  }
@@ -80,7 +80,9 @@ export function expandedEndpointBodyTemplate(path, tagName = '', tagDescription
80
80
  ? html`
81
81
  <div style="display:flex; flex-wrap:wrap; margin-bottom: -24px; font-size: var(--font-size-small);">
82
82
  ${path.xBadges.map((v) => (
83
- html`<span style="margin:1px; margin-right:5px; padding:1px 8px; font-weight:bold; border-radius:12px; background-color: var(--light-${v.color}, var(--input-bg)); color:var(--${v.color}); border:1px solid var(--${v.color})">${v.label}</span>`
83
+ v.color === 'none'
84
+ ? ''
85
+ : html`<span style="margin:1px; margin-right:5px; padding:1px 8px; font-weight:bold; border-radius:12px; background-color: var(--light-${v.color}, var(--input-bg)); color:var(--${v.color}); border:1px solid var(--${v.color})">${v.label}</span>`
84
86
  ))
85
87
  }
86
88
  </div>
@@ -126,7 +128,7 @@ export function expandedEndpointBodyTemplate(path, tagName = '', tagDescription
126
128
  .request_body = "${path.requestBody}"
127
129
  .api_keys = "${nonEmptyApiKeys}"
128
130
  .servers = "${path.servers}"
129
- server-url = "${path.servers?.[0]?.url || this.selectedServer.computedUrl}"
131
+ server-url = "${path.servers?.[0]?.url || this.selectedServer?.computedUrl}"
130
132
  fill-request-fields-with-example = "${this.fillRequestFieldsWithExample}"
131
133
  allow-try = "${this.allowTry}"
132
134
  show-curl-before-try = "${this.showCurlBeforeTry}"
@@ -5,7 +5,7 @@ import { marked } from 'marked';
5
5
  // Templates
6
6
  import overviewTemplate from '~/templates/overview-template';
7
7
  import headerTemplate from '~/templates/header-template';
8
- import { schemaInObjectNotation, generateExample } from '~/utils/schema-utils';
8
+ import { schemaInObjectNotation, generateExample, standardizeExample } from '~/utils/schema-utils';
9
9
  import '~/components/json-tree';
10
10
  import '~/components/schema-tree';
11
11
  import SetTheme from '~/utils/theme';
@@ -49,7 +49,7 @@ function jsonSchemaBodyTemplate() {
49
49
  ${this.showInfo === 'true' ? overviewTemplate.call(this) : ''}
50
50
  <div style="font-size:var(--font-size-regular);">
51
51
  ${this.resolvedSpec.schemaAndExamples.map((jSchemaBody) => {
52
- const examplesObj = generateExample(jSchemaBody.schema, 'json', jSchemaBody.examples, jSchemaBody.example, true, false, 'json', true);
52
+ const examplesObj = generateExample(jSchemaBody.schema, 'json', standardizeExample(jSchemaBody.examples), standardizeExample(jSchemaBody.example), true, false, 'json', true);
53
53
  jSchemaBody.selectedExample = examplesObj[0]?.exampleId;
54
54
  return html`
55
55
  <section id='${jSchemaBody.elementId}' class='json-schema-and-example regular-font' style="display:flex; flex-direction: column; border:1px solid var(--border-color); margin-bottom:32px; border-top: 5px solid var(--border-color)">
@@ -99,7 +99,7 @@ function jsonSchemaBodyTemplate() {
99
99
 
100
100
  // Json Schema Root Template
101
101
  export default function jsonSchemaViewerTemplate(isMini = false) {
102
- // export default function jsonSchemaViewerTemplate(isMini = false, showExpandCollapse = true, showTags = true, pathsExpanded = false) {
102
+ // export default function jsonSchemaViewerTemplate(isMini = false, pathsExpanded = false) {
103
103
  if (!this.resolvedSpec) {
104
104
  return '';
105
105
  }
@@ -13,7 +13,7 @@ import advancedSearchTemplate from '~/templates/advance-search-template';
13
13
  import SetTheme from '~/utils/theme';
14
14
  import { isValidHexColor } from '~/utils/color-utils';
15
15
 
16
- export default function mainBodyTemplate(isMini = false, showExpandCollapse = true, showTags = true, pathsExpanded = false) {
16
+ export default function mainBodyTemplate(isMini = false, pathsExpanded = false) {
17
17
  if (!this.resolvedSpec) {
18
18
  return '';
19
19
  }
@@ -104,7 +104,7 @@ export default function mainBodyTemplate(isMini = false, showExpandCollapse = tr
104
104
  </div>
105
105
  ${this.renderStyle === 'read'
106
106
  ? expandedEndpointTemplate.call(this)
107
- : endpointTemplate.call(this, showExpandCollapse, showTags, pathsExpanded)
107
+ : endpointTemplate.call(this, isMini, pathsExpanded)
108
108
  }
109
109
  `
110
110
  }
@@ -1,6 +1,6 @@
1
1
  import { html } from 'lit';
2
2
  import { marked } from 'marked';
3
- import { pathIsInSearch } from '~/utils/common-utils';
3
+ import { getMatchedPaths } from '~/utils/common-utils';
4
4
 
5
5
  export function expandCollapseNavBarTag(navLinkEl, action = 'toggle') {
6
6
  const tagAndPathEl = navLinkEl?.closest('.nav-bar-tag-and-paths');
@@ -82,7 +82,7 @@ export default function navbarTemplate() {
82
82
  >
83
83
  <div style='margin: 6px 5px 0 -24px; font-size:var(--font-size-regular); cursor:pointer;'>&#x21a9;</div>
84
84
  </div>
85
- ${this.matchPaths
85
+ ${this.searchVal
86
86
  ? html`
87
87
  <button @click = '${this.onClearSearch}' class='m-btn thin-border' style='margin-left:5px; color:var(--nav-text-color); width:75px; padding:6px 8px;' part='btn btn-outline btn-clear-filter'>
88
88
  CLEAR
@@ -91,7 +91,7 @@ export default function navbarTemplate() {
91
91
  }
92
92
  `
93
93
  }
94
- ${this.allowAdvancedSearch === 'false' || this.matchPaths
94
+ ${this.allowAdvancedSearch === 'false' || this.searchVal
95
95
  ? ''
96
96
  : html`
97
97
  <button class='m-btn primary' part='btn btn-fill btn-search' style='margin-left:5px; padding:6px 8px; width:75px' @click='${this.onShowSearchModalClicked}'>
@@ -166,7 +166,7 @@ export default function navbarTemplate() {
166
166
 
167
167
  <!-- TAGS AND PATHS-->
168
168
  ${this.resolvedSpec.tags
169
- .filter((tag) => tag.paths.filter((path) => pathIsInSearch(this.matchPaths, path, this.matchType)).length)
169
+ .filter((tag) => tag.paths.filter((path) => getMatchedPaths(this.searchVal, path, tag.name)).length)
170
170
  .map((tag) => html`
171
171
  <div class='nav-bar-tag-and-paths ${(this.renderStyle === 'read' ? 'expanded' : (tag.expanded ? 'expanded' : 'collapsed'))}' >
172
172
  ${tag.name === 'General ⦂'
@@ -208,8 +208,8 @@ export default function navbarTemplate() {
208
208
  <div class='nav-bar-paths-under-tag' style='max-height:${(tag.expanded || this.renderStyle === 'read') ? ((tag.paths?.length || 1) * 50) : 0}px;'>
209
209
  <!-- Paths in each tag (endpoints) -->
210
210
  ${tag.paths.filter((v) => {
211
- if (this.matchPaths) {
212
- return pathIsInSearch(this.matchPaths, v, this.matchType);
211
+ if (this.searchVal) {
212
+ return getMatchedPaths(this.searchVal, v, tag.name);
213
213
  }
214
214
  return true;
215
215
  }).map((p) => html`
@@ -134,7 +134,7 @@ async function fetchAccessToken(tokenUrl, clientId, clientSecret, redirectUrl, g
134
134
  }
135
135
  return false;
136
136
  }
137
- } catch (err) {
137
+ } catch {
138
138
  if (respDisplayEl) {
139
139
  respDisplayEl.innerHTML = '<span style="color:var(--red)">Failed to get access token</span>';
140
140
  }
@@ -261,7 +261,7 @@ function oAuthFlowTemplate(flowName, clientId, clientSecret, securitySchemeId, a
261
261
  const pkceOnly = authFlow['x-pkce-only'] || false;
262
262
  const isUrlAbsolute = (url) => (url.indexOf('://') > 0 || url.indexOf('//') === 0);
263
263
  // Calculcate base URL
264
- const url = new URL(this.selectedServer.computedUrl);
264
+ const url = new URL(this.selectedServer?.computedUrl);
265
265
  const baseUrl = url.origin;
266
266
 
267
267
  if (refreshUrl && !isUrlAbsolute(refreshUrl)) {
@@ -322,8 +322,8 @@ function oAuthFlowTemplate(flowName, clientId, clientSecret, securitySchemeId, a
322
322
  ${flowName === 'password'
323
323
  ? html`
324
324
  <div style="margin:5px 0">
325
- <input type="text" value = "" placeholder="username" spellcheck="false" class="oauth2 ${flowName} ${securitySchemeId} api-key-user" part="textbox textbox-username">
326
- <input type="password" value = "" placeholder="password" spellcheck="false" class="oauth2 ${flowName} ${securitySchemeId} api-key-password" style = "margin:0 5px;" part="textbox textbox-password">
325
+ <input type="text" value = "" placeholder="username" spellcheck="false" class="oauth2 ${flowName} ${securitySchemeId} api-key-user" part="textbox textbox-username" id="input-${securitySchemeId}-${flowName}-api-key-user">
326
+ <input type="password" value = "" placeholder="password" spellcheck="false" class="oauth2 ${flowName} ${securitySchemeId} api-key-password" style = "margin:0 5px;" part="textbox textbox-password" id="input-${securitySchemeId}-${flowName}-api-key-password">
327
327
  </div>`
328
328
  : ''
329
329
  }
@@ -343,6 +343,7 @@ function oAuthFlowTemplate(flowName, clientId, clientSecret, securitySchemeId, a
343
343
  ${flowName === 'authorizationCode' || flowName === 'clientCredentials' || flowName === 'password'
344
344
  ? html`
345
345
  <input
346
+ id="${securitySchemeId}-${flowName}-oauth-client-secret"
346
347
  type="password" part="textbox textbox-auth-client-secret"
347
348
  value = "${clientSecret || ''}" placeholder="client-secret" spellcheck="false"
348
349
  class="oauth2 ${flowName} ${securitySchemeId}
@@ -439,7 +440,7 @@ export default function securitySchemeTemplate() {
439
440
  <div style="max-height:28px;">
440
441
  ${v.in !== 'cookie'
441
442
  ? html`
442
- <input type = "text" value = "${v.value}" class="${v.type} ${v.securitySchemeId} api-key-input" placeholder = "api-token" spellcheck = "false">
443
+ <input type = "text" value = "${v.value}" class="${v.type} ${v.securitySchemeId} api-key-input" placeholder = "api-token" spellcheck = "false" id = "${v.type}-${v.securitySchemeId}-api-key-input">
443
444
  <button class="m-btn thin-border" style = "margin-left:5px;"
444
445
  part = "btn btn-outline"
445
446
  @click="${(e) => { onApiKeyChange.call(this, v.securitySchemeId, e); }}">
@@ -456,8 +457,8 @@ export default function securitySchemeTemplate() {
456
457
  Send <code>Authorization</code> in <code>header</code> containing the word <code>Basic</code> followed by a space and a base64 encoded string of <code>username:password</code>.
457
458
  </div>
458
459
  <div>
459
- <input type="text" value = "${v.user}" placeholder="username" spellcheck="false" class="${v.type} ${v.securitySchemeId} api-key-user" style="width:100px">
460
- <input type="password" value = "${v.password}" placeholder="password" spellcheck="false" class="${v.type} ${v.securitySchemeId} api-key-password" style = "width:100px; margin:0 5px;">
460
+ <input type="text" value = "${v.user}" placeholder="username" spellcheck="false" class="${v.type} ${v.securitySchemeId} api-key-user" style="width:100px" id = "input-${v.type}-${v.securitySchemeId}-api-key-user">
461
+ <input type="password" value = "${v.password}" placeholder="password" spellcheck="false" class="${v.type} ${v.securitySchemeId} api-key-password" style = "width:100px; margin:0 5px;" id = "input-${v.type}-${v.securitySchemeId}-api-key-password">
461
462
  <button class="m-btn thin-border"
462
463
  @click="${(e) => { onApiKeyChange.call(this, v.securitySchemeId, e); }}"
463
464
  part = "btn btn-outline"
@@ -600,6 +601,7 @@ export function pathSecurityTemplate(pathSecurity) {
600
601
  `)
601
602
  }
602
603
  </div>
604
+ </div>
603
605
  `;
604
606
  }
605
607
  return '';
@@ -49,17 +49,13 @@ export async function wait(ms) {
49
49
  });
50
50
  }
51
51
 
52
- export function componentIsInSearch(searchVal, component) {
52
+ export function getMatchedComponents(searchVal, component) {
53
53
  return component.name.toLowerCase().includes(searchVal.toLowerCase());
54
54
  }
55
55
 
56
- export function pathIsInSearch(searchVal, path, matchType = 'includes') {
57
- if (matchType === 'includes') {
58
- const stringToSearch = `${path.method} ${path.path} ${path.summary || ''} ${path.description || ''} ${path.operationId || ''}`.toLowerCase();
59
- return stringToSearch.includes(searchVal.toLowerCase());
60
- }
61
- const regex = new RegExp(searchVal, 'i');
62
- return regex.test(`${path.method} ${path.path}`);
56
+ export function getMatchedPaths(searchVal, path, tagName = '') {
57
+ const stringToSearch = `${path.method} ${path.path} ${path.summary || ''} ${path.description || ''} ${path.operationId || ''} ${tagName}`.toLowerCase();
58
+ return stringToSearch.includes(searchVal.toLowerCase());
63
59
  }
64
60
 
65
61
  export function schemaKeys(schemaProps, result = new Set()) {
@@ -90,7 +86,7 @@ export function advancedSearch(searchVal, allSpecTags, searchOptions = []) {
90
86
  stringToSearch = path.path;
91
87
  }
92
88
  if (searchOptions.includes('search-api-descr')) {
93
- stringToSearch = `${stringToSearch} ${path.summary || path.description || ''}`;
89
+ stringToSearch = `${stringToSearch} ${(path.summary || '') + (path.description || '')}`;
94
90
  }
95
91
  if (searchOptions.includes('search-api-params')) {
96
92
  stringToSearch = `${stringToSearch} ${path.parameters?.map((v) => v.name).join(' ') || ''}`;
@@ -120,18 +120,67 @@ export function getTypeInfo(schema) {
120
120
  return info;
121
121
  }
122
122
 
123
- export function nestExampleIfPresent(example) {
124
- if (typeof example === 'boolean' || typeof example === 'number') {
125
- return {
126
- Example: { value: `${example}` },
127
- };
128
- }
129
- if (example === '') {
130
- return {
131
- Example: { value: '' },
132
- };
123
+ /**
124
+ *
125
+ * @param {*} ex if the value
126
+ * - Is an Object with 'value' property like
127
+ * { 'value': 'example_val1', 'description': 'some description' }
128
+ * Returns >>>
129
+ * {
130
+ * 'Example': { 'value' : 'example_val1', 'description': 'some description' },
131
+ * }
132
+ * - Is an object where each key represents a valid example object (i,e has a value property)
133
+ * {
134
+ * 'example1': { 'value' : 'example_val1', 'description': 'some description' },
135
+ * 'example2': { 'value' : 'example_val2', 'description': 'some other description' },
136
+ * 'invalid': { 'description': 'invalid example object without any value property' }
137
+ * }
138
+ * Returns >>>
139
+ * {
140
+ * 'example1': { 'value' : 'example_val1', 'description': 'some description' },
141
+ * 'example2': { 'value' : 'example_val2', 'description': 'some other description' }
142
+ * }
143
+ * if none of the keys represents an object with 'value' property then return undefined
144
+ * - Is an array of premitive values
145
+ * ['example_val1', 'example_val2']
146
+ * Returns >>>
147
+ * {
148
+ * 'Example1': {value:'value1'}
149
+ * 'Example2': {value:'value2'}
150
+ * }
151
+ * - Is a premitive value
152
+ * 'example_val1'
153
+ * Returns >>>
154
+ * {
155
+ * 'Example': { 'value': 'example_val1' }
156
+ * }
157
+ * - Is undefined
158
+ * returns undefined
159
+ * @returns
160
+ */
161
+
162
+ export function standardizeExample(ex) {
163
+ if (typeof ex === 'object' && !Array.isArray(ex)) {
164
+ if (ex.value !== undefined) {
165
+ // Case 1: Single object with 'value' property
166
+ return { Example: { ...ex } };
167
+ }
168
+ // Case 2: Object where each key is an object with a 'value' property
169
+ const filteredEntries = Object.entries(ex).filter(([_, obj]) => obj.value !== undefined); // eslint-disable-line
170
+ // If no valid entries found, return JSON.stringify of the input
171
+ if (filteredEntries.length === 0) {
172
+ return undefined;
173
+ }
174
+ return Object.fromEntries(filteredEntries);
175
+ } if (Array.isArray(ex)) {
176
+ // Case 3: Array of primitive values
177
+ return ex.reduce((acc, value, index) => {
178
+ acc[`Example${index + 1}`] = { value };
179
+ return acc;
180
+ }, {});
133
181
  }
134
- return example ? { Example: { value: example } } : example;
182
+ // Case 4: Single primitive value
183
+ return ex ? { Example: { value: ex } } : undefined;
135
184
  }
136
185
 
137
186
  /**
@@ -268,7 +317,7 @@ export function getSampleValueByType(schemaObj) {
268
317
  if (schemaObj.pattern) {
269
318
  try {
270
319
  return new RandExp(schemaObj.pattern).gen();
271
- } catch (error) {
320
+ } catch {
272
321
  return schemaObj.pattern;
273
322
  }
274
323
  }
@@ -297,7 +346,7 @@ export function getSampleValueByType(schemaObj) {
297
346
  case 'ipv6':
298
347
  return '2001:0db8:5b96:0000:0000:426f:8e17:642a';
299
348
  case 'uuid':
300
- return [u.substr(0, 8), u.substr(8, 4), `4000-8${u.substr(13, 3)}`, u.substr(16, 12)].join('-');
349
+ return [u.substring(0, 8), u.substring(8, 12), `4000-8${u.substring(13, 16)}`, u.substring(16, 28)].join('-');
301
350
  case 'byte':
302
351
  return 'ZXhhbXBsZQ=='; // 'example' base64 encoded. See https://spec.openapis.org/oas/v3.0.0#data-types
303
352
  default:
@@ -857,7 +906,7 @@ export function schemaInObjectNotation(schema, obj, level = 0, suffix = '') {
857
906
  }
858
907
 
859
908
  /* Create Example object */
860
- export function generateExample(schema, mimeType, examples = '', example = '', includeReadOnly = true, includeWriteOnly = true, outputType = 'json', includeGeneratedExample = false) {
909
+ export function generateExample(schema, mimeType, examples = {}, example = {}, includeReadOnly = true, includeWriteOnly = true, outputType = 'json', includeGeneratedExample = false) {
861
910
  const finalExamples = [];
862
911
  // First check if examples is provided
863
912
  if (examples) {
@@ -876,7 +925,7 @@ export function generateExample(schema, mimeType, examples = '', example = '', i
876
925
  const fixedJsonString = examples[eg].value;
877
926
  egContent = JSON.parse(fixedJsonString);
878
927
  egFormat = 'json';
879
- } catch (err) {
928
+ } catch {
880
929
  egFormat = 'text';
881
930
  egContent = examples[eg].value;
882
931
  }
@@ -910,7 +959,7 @@ export function generateExample(schema, mimeType, examples = '', example = '', i
910
959
  try {
911
960
  egContent = JSON.parse(example);
912
961
  egFormat = 'json';
913
- } catch (err) {
962
+ } catch {
914
963
  egFormat = 'text';
915
964
  egContent = example;
916
965
  }
@@ -3,8 +3,22 @@ import OpenApiParser from '@apitools/openapi-parser';
3
3
  import { marked } from 'marked';
4
4
  import { invalidCharsRegEx, rapidocApiKey, sleep } from '~/utils/common-utils';
5
5
 
6
- export default async function ProcessSpec(specUrl, generateMissingTags = false, sortTags = false, sortSchemas = false, sortEndpointsBy = '', attrApiKey = '', attrApiKeyLocation = '', attrApiKeyValue = '', serverUrl = '') {
6
+ export default async function ProcessSpec(
7
+ specUrl,
8
+ generateMissingTags = false,
9
+ sortTags = false,
10
+ sortSchemas = false,
11
+ sortEndpointsBy = '',
12
+ attrApiKey = '',
13
+ attrApiKeyLocation = '',
14
+ attrApiKeyValue = '',
15
+ serverUrl = '',
16
+ matchPaths = '',
17
+ matchType = '',
18
+ removeEndpointsWithBadgeLabelAs = '',
19
+ ) {
7
20
  let jsonParsedSpec;
21
+
8
22
  try {
9
23
  this.requestUpdate(); // important to show the initial loader
10
24
  let specMeta;
@@ -15,7 +29,7 @@ export default async function ProcessSpec(specUrl, generateMissingTags = false,
15
29
  }
16
30
  await sleep(0); // important to show the initial loader (allows for rendering updates)
17
31
 
18
- // If JSON Schema Viewer
32
+ // If JSON Schema Viewer
19
33
  if (specMeta.resolvedSpec?.jsonSchemaViewer && specMeta.resolvedSpec?.schemaAndExamples) {
20
34
  this.dispatchEvent(new CustomEvent('before-render', { detail: { spec: specMeta.resolvedSpec } }));
21
35
  const schemaAndExamples = Object.entries(specMeta.resolvedSpec.schemaAndExamples).map((v) => ({ show: true, expanded: true, selectedExample: null, name: v[0], elementId: v[0].replace(invalidCharsRegEx, '-'), ...v[1] }));
@@ -27,8 +41,10 @@ export default async function ProcessSpec(specUrl, generateMissingTags = false,
27
41
  };
28
42
  return parsedSpec;
29
43
  }
44
+
45
+ // If RapiDoc or RapiDocMini
30
46
  if (specMeta.spec && (specMeta.spec.components || specMeta.spec.info || specMeta.spec.servers || specMeta.spec.tags || specMeta.spec.paths)) {
31
- jsonParsedSpec = specMeta.spec;
47
+ jsonParsedSpec = filterPaths(specMeta.spec, matchPaths, matchType, removeEndpointsWithBadgeLabelAs);
32
48
  this.dispatchEvent(new CustomEvent('before-render', { detail: { spec: jsonParsedSpec } }));
33
49
  } else {
34
50
  console.info('RapiDoc: %c There was an issue while parsing the spec %o ', 'color:orangered', specMeta); // eslint-disable-line no-console
@@ -112,7 +128,7 @@ export default async function ProcessSpec(specUrl, generateMissingTags = false,
112
128
 
113
129
  // Servers
114
130
  let servers = [];
115
- if (jsonParsedSpec.servers && Array.isArray(jsonParsedSpec.servers)) {
131
+ if (jsonParsedSpec.servers && Array.isArray(jsonParsedSpec.servers) && jsonParsedSpec.servers.length > 0) {
116
132
  jsonParsedSpec.servers.forEach((v) => {
117
133
  let computedUrl = v.url.trim();
118
134
  if (!(computedUrl.startsWith('http') || computedUrl.startsWith('//') || computedUrl.startsWith('{'))) {
@@ -141,7 +157,7 @@ export default async function ProcessSpec(specUrl, generateMissingTags = false,
141
157
  } else {
142
158
  jsonParsedSpec.servers = [{ url: 'http://localhost', computedUrl: 'http://localhost' }];
143
159
  }
144
- servers = jsonParsedSpec.servers;
160
+ servers = jsonParsedSpec.servers; // eslint-disable-line prefer-destructuring
145
161
  const parsedSpec = {
146
162
  specLoadError: false,
147
163
  isSpecLoading: false,
@@ -156,6 +172,60 @@ export default async function ProcessSpec(specUrl, generateMissingTags = false,
156
172
  return parsedSpec;
157
173
  }
158
174
 
175
+ function filterPaths(openApiObject, matchPaths = '', matchType = '', removeEndpointsWithBadgeLabelAs = '') {
176
+ const filteredPaths = {};
177
+
178
+ // Convert the removePathsWithBadgeLabeledAs to an array if provided
179
+ const labelsToRemove = removeEndpointsWithBadgeLabelAs.split(',').map((label) => label.trim().toLowerCase()).filter(Boolean);
180
+
181
+ // Helper function to check if a path should be included based on matchPaths
182
+ function pathMatches(pathsKey, httpMethod) {
183
+ if (!matchPaths) {
184
+ return true; // If no matchPaths provided, include everything
185
+ }
186
+ const fullPath = `${httpMethod} ${pathsKey}`.toLowerCase(); // Construct "method path" string
187
+ if (matchType === 'regex') {
188
+ const regex = new RegExp(matchPaths, 'i');
189
+ return regex.test(matchPaths.toLowerCase());
190
+ }
191
+ return fullPath.includes(matchPaths.toLowerCase());
192
+ }
193
+
194
+ // Helper function to check if the badges contain any label that needs to be removed
195
+ function containsLabelToRemove(badges) {
196
+ return badges.some((badge) => labelsToRemove.includes(badge?.label.toLowerCase()));
197
+ }
198
+
199
+ // Loop through the paths in the openApiObject
200
+ Object.entries(openApiObject.paths).forEach(([pathsKey, methods]) => {
201
+ const filteredMethods = {};
202
+
203
+ Object.entries(methods).forEach(([httpMethod, methodDetails]) => {
204
+ const badges = methodDetails['x-badges'];
205
+
206
+ // Filter by matchPaths
207
+ if (pathMatches(pathsKey, httpMethod)) {
208
+ if (badges && Array.isArray(badges)) {
209
+ // Filter out based on removePathsWithBadgeLabeledAs
210
+ if (!containsLabelToRemove(badges)) {
211
+ filteredMethods[httpMethod] = methodDetails;
212
+ }
213
+ } else {
214
+ // No badges present, include the method
215
+ filteredMethods[httpMethod] = methodDetails;
216
+ }
217
+ }
218
+ });
219
+
220
+ if (Object.keys(filteredMethods).length > 0) {
221
+ filteredPaths[pathsKey] = filteredMethods;
222
+ }
223
+ });
224
+
225
+ openApiObject.paths = filteredPaths;
226
+ return openApiObject;
227
+ }
228
+
159
229
  function getHeadersFromMarkdown(markdownContent) {
160
230
  const tokens = marked.lexer(markdownContent);
161
231
  const headers = tokens.filter((v) => v.type === 'heading' && v.depth <= 2);
@@ -245,7 +315,7 @@ function getComponents(openApiSpec, sortSchemas = false) {
245
315
 
246
316
  function groupByTags(openApiSpec, sortEndpointsBy, generateMissingTags = false, sortTags = false) {
247
317
  const supportedMethods = ['get', 'put', 'post', 'delete', 'patch', 'head', 'options']; // this is also used for ordering endpoints by methods
248
- const tags = openApiSpec.tags && Array.isArray(openApiSpec.tags)
318
+ const tags = openApiSpec.tags && Array.isArray(openApiSpec.tags) && openApiSpec.tags.length > 0
249
319
  ? openApiSpec.tags.map((v) => ({
250
320
  show: true,
251
321
  elementId: `tag--${v.name.replace(invalidCharsRegEx, '-')}`,
@@ -286,7 +356,7 @@ function groupByTags(openApiSpec, sortEndpointsBy, generateMissingTags = false,
286
356
  pathTags.push(pathOrHookNameKey);
287
357
  } else {
288
358
  // firstWordEndIndex -= 1;
289
- pathTags.push(pathOrHookNameKey.substr(0, firstWordEndIndex));
359
+ pathTags.push(pathOrHookNameKey.substring(0, firstWordEndIndex));
290
360
  }
291
361
  } else {
292
362
  pathTags.push('General ⦂');
@@ -21,8 +21,8 @@ export default function setTheme(baseTheme, theme = {}) {
21
21
  const fg3 = theme.fg3 ? theme.fg3 : ColorUtils.color.brightness(fg1, -20); // or #aaa
22
22
  const lightFg = theme.fg3 ? theme.fg3 : ColorUtils.color.brightness(fg1, -65); // or #777
23
23
  const inlineCodeFg = theme.inlineCodeFg ? theme.inlineCodeFg : '#aaa';
24
- const selectionBg = '#bbb';
25
- const selectionFg = '#eee';
24
+ const selectionBg = fg2;
25
+ const selectionFg = bg2;
26
26
 
27
27
  const headerColor = theme.headerColor ? theme.headerColor : ColorUtils.color.brightness(bg1, 10);
28
28
 
@@ -107,10 +107,8 @@ export default function setTheme(baseTheme, theme = {}) {
107
107
  const fg3 = theme.fg3 ? theme.fg3 : ColorUtils.color.brightness(fg1, 30); // or #666
108
108
  const lightFg = theme.fg3 ? theme.fg3 : ColorUtils.color.brightness(fg1, 70); // or #999
109
109
  const inlineCodeFg = theme.inlineCodeFg ? theme.inlineCodeFg : 'brown';
110
-
111
- const selectionBg = '#444';
112
- const selectionFg = '#eee';
113
-
110
+ const selectionBg = fg2;
111
+ const selectionFg = bg2;
114
112
  const headerColor = theme.headerColor ? theme.headerColor : ColorUtils.color.brightness(bg1, -180);
115
113
 
116
114
  /*