rapidoc 11.0.0 → 12.0.0

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 (80) hide show
  1. package/README.md +46 -31
  2. package/dist/rapidoc-min.js +1424 -213
  3. package/dist/rapidoc-static-min.js +13542 -0
  4. package/package.json +48 -34
  5. package/src/components/api-request.ts +869 -0
  6. package/src/components/api-response.ts +509 -0
  7. package/src/components/json-tree.ts +171 -0
  8. package/src/components/schema-base-element.ts +315 -0
  9. package/src/components/schema-table.ts +430 -0
  10. package/src/components/schema-tree.ts +386 -0
  11. package/src/components/tag-input.ts +184 -0
  12. package/src/index.js +6 -0
  13. package/src/oauth-receiver.ts +29 -0
  14. package/src/rapidoc-mini.ts +582 -0
  15. package/src/rapidoc.ts +1808 -0
  16. package/src/static/index.d.ts +149 -0
  17. package/src/static/index.js +62 -0
  18. package/src/static/navigation.js +497 -0
  19. package/src/static/render.js +490 -0
  20. package/src/static/serializer.js +216 -0
  21. package/src/styles/auth-styles.js +318 -0
  22. package/src/styles/border-styles.js +24 -0
  23. package/src/styles/css-bundle.js +129 -0
  24. package/src/styles/custom-styles.js +8 -0
  25. package/src/styles/dialog-box-styles.js +102 -0
  26. package/src/styles/endpoint-styles.js +559 -0
  27. package/src/styles/flex-styles.js +16 -0
  28. package/src/styles/font-styles.js +432 -0
  29. package/src/styles/info-styles.js +36 -0
  30. package/src/styles/input-styles.js +555 -0
  31. package/src/styles/layout-styles.js +396 -0
  32. package/src/styles/microlighter-styles.js +99 -0
  33. package/src/styles/nav-styles.js +609 -0
  34. package/src/styles/param-list-styles.js +261 -0
  35. package/src/styles/schema-styles.js +288 -0
  36. package/src/styles/tab-styles.js +106 -0
  37. package/src/styles/theme-tokens.js +582 -0
  38. package/src/templates/advanced-search-dialog-template.js +94 -0
  39. package/src/templates/api-response-template.js +219 -0
  40. package/src/templates/callback-template.js +86 -0
  41. package/src/templates/code-samples-template.js +52 -0
  42. package/src/templates/components-template.js +86 -0
  43. package/src/templates/downloads-template.js +85 -0
  44. package/src/templates/endpoint-template.js +386 -0
  45. package/src/templates/expanded-endpoint-template.js +266 -0
  46. package/src/templates/focused-endpoint-template.js +145 -0
  47. package/src/templates/header-template.js +92 -0
  48. package/src/templates/logo-template.js +21 -0
  49. package/src/templates/main-body-template.js +102 -0
  50. package/src/templates/navbar-template.js +489 -0
  51. package/src/templates/overview-template.js +104 -0
  52. package/src/templates/request-body-template.js +651 -0
  53. package/src/templates/request-params-template.js +474 -0
  54. package/src/templates/security-scheme-template.js +1263 -0
  55. package/src/templates/server-template.js +152 -0
  56. package/src/utils/auto-color-scheme.js +99 -0
  57. package/src/utils/bind-navbar.js +175 -0
  58. package/src/utils/color-utils.js +59 -0
  59. package/src/utils/common-utils.js +191 -0
  60. package/src/utils/curl-utils.js +123 -0
  61. package/src/utils/highlighter.js +416 -0
  62. package/src/utils/markdown-utils.js +110 -0
  63. package/src/utils/mcp/common.js +467 -0
  64. package/src/utils/mcp/generators/csharp.js +242 -0
  65. package/src/utils/mcp/generators/go.js +298 -0
  66. package/src/utils/mcp/generators/java.js +330 -0
  67. package/src/utils/mcp/generators/python.js +248 -0
  68. package/src/utils/mcp/generators/rust.js +281 -0
  69. package/src/utils/mcp/generators/typescript.js +290 -0
  70. package/src/utils/mcp-generator.js +143 -0
  71. package/src/utils/mock-interceptor.js +508 -0
  72. package/src/utils/oauth-utils.js +87 -0
  73. package/src/utils/request-utils.js +203 -0
  74. package/src/utils/response-utils.js +203 -0
  75. package/src/utils/sanitize.js +57 -0
  76. package/src/utils/schema-utils.js +1927 -0
  77. package/src/utils/slugger.js +189 -0
  78. package/src/utils/spec-parser.js +915 -0
  79. package/src/utils/url-utils.js +211 -0
  80. package/src/utils/xml-utils.js +61 -0
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Renders the API server selector dropdown and server variable configuration form.
3
+ */
4
+ import { html } from "lit";
5
+ import { unsafeHTML } from "lit/directives/unsafe-html.js";
6
+ import { sanitizeHTML } from "../utils/sanitize.js";
7
+ import { marked } from "marked";
8
+
9
+ export function setApiServer(serverUrl) {
10
+ const serverObj = this.resolvedSpec?.servers.find((s) => s.url === serverUrl);
11
+ if (!serverObj) {
12
+ return false;
13
+ }
14
+ this.selectedServer = serverObj;
15
+ this.requestUpdate();
16
+ this.dispatchEvent(
17
+ new CustomEvent("api-server-change", {
18
+ bubbles: true,
19
+ composed: true,
20
+ detail: {
21
+ selectedServer: serverObj,
22
+ },
23
+ }),
24
+ );
25
+ return true;
26
+ }
27
+
28
+ function onApiServerVarChange(e, serverObj) {
29
+ const inputEls = [...e.currentTarget.closest("table").querySelectorAll("input, select")];
30
+ let tempUrl = serverObj.url;
31
+ inputEls.forEach((v) => {
32
+ const regex = new RegExp(`{${v.dataset.var}}`, "g");
33
+ tempUrl = tempUrl.replace(regex, v.value);
34
+ });
35
+ serverObj.computedUrl = tempUrl;
36
+ this.requestUpdate();
37
+ }
38
+
39
+ function serverVarsTemplate() {
40
+ // const selectedServerObj = this.resolvedSpec.servers.find((v) => (v.url === this.selectedServer));
41
+ return this.selectedServer && this.selectedServer.variables
42
+ ? html`
43
+ <h3 class="rapidoc-heading label-heading table-title" part="heading heading-3">Server variables</h3>
44
+ <div class="param-list-container">
45
+ <ul class="param-list server-vars" part="param-list" role="list">
46
+ ${Object.entries(this.selectedServer.variables).map(
47
+ (kv) => html`
48
+ <li class="param" part="param">
49
+ <div class="param-label" part="param-label">
50
+ <div class="param-name" part="param-name">${kv[0]}</div>
51
+ </div>
52
+ <div class="param-input" part="param-input">
53
+ ${
54
+ kv[1].enum
55
+ ? html` <select
56
+ data-var="${kv[0]}"
57
+ @input=${(e) => {
58
+ onApiServerVarChange.call(this, e, this.selectedServer);
59
+ }}
60
+ >
61
+ ${Object.entries(kv[1].enum).map((e) =>
62
+ kv[1].default === e[1]
63
+ ? html`<option selected label=${e[1]} value=${e[1]}></option>`
64
+ : html`<option label=${e[1]} value=${e[1]}></option>`,
65
+ )}
66
+ </select>`
67
+ : html` <input
68
+ type="text"
69
+ part="textbox textbox-server-var"
70
+ spellcheck="false"
71
+ data-var="${kv[0]}"
72
+ value="${kv[1].default}"
73
+ @input=${(e) => {
74
+ onApiServerVarChange.call(this, e, this.selectedServer);
75
+ }}
76
+ />`
77
+ }
78
+ </div>
79
+ ${
80
+ kv[1].description
81
+ ? html`<div class="param-details" part="param-details">
82
+ <div class="m-markdown-small">${unsafeHTML(sanitizeHTML(marked(kv[1].description)))}</div>
83
+ </div>`
84
+ : ""
85
+ }
86
+ </li>
87
+ `,
88
+ )}
89
+ </ul>
90
+ </div>
91
+ `
92
+ : "";
93
+ }
94
+
95
+ export default function serverTemplate() {
96
+ if (!this.resolvedSpec || this.resolvedSpec.specLoadError) {
97
+ return "";
98
+ }
99
+ return html`<section
100
+ id="servers"
101
+ part="section-servers"
102
+ style="text-align:left; direction:ltr; margin-top:24px; margin-bottom:24px;"
103
+ class="regular-font observe-me ${"read focused".includes(this.renderStyle) ? "section-gap--read-mode" : "section-gap"}"
104
+ >
105
+ <h2 part="section-servers-title heading heading-2" class="rapidoc-heading sub-title" style="display:flex; align-items:center; gap:8px;">
106
+ API Server
107
+ ${
108
+ this.mockServer === "true"
109
+ ? html`<span
110
+ style="font-size:var(--font-size-small); font-weight:bold; color:var(--green); border:1px solid var(--green); border-radius:var(--radius); padding:1px 6px; letter-spacing:0.5px;"
111
+ title="In-browser mock server is active"
112
+ >
113
+ MOCK ACTIVE
114
+ </span>`
115
+ : ""
116
+ }
117
+ </h2>
118
+ <div class="mono-font" style="margin: 12px 0; font-size:calc(var(--font-size-small) + 1px);">
119
+ ${
120
+ !this.resolvedSpec.servers || this.resolvedSpec.servers?.length === 0
121
+ ? ""
122
+ : html`
123
+ ${this.resolvedSpec?.servers.map(
124
+ (server, i) => html`
125
+ <div class="server-opt-row">
126
+ <input
127
+ type="radio"
128
+ name="api_server"
129
+ id="srvr-opt-${i}"
130
+ value="${server.url}"
131
+ @change=${() => {
132
+ setApiServer.call(this, server.url);
133
+ }}
134
+ .checked="${this.selectedServer.url === server.url}"
135
+ />
136
+ <label for="srvr-opt-${i}">
137
+ ${server.url} ${server.description ? html`- <span class="regular-font">${server.description} </span>` : ""}
138
+ </label>
139
+ </div>
140
+ `,
141
+ )}
142
+ `
143
+ }
144
+ <div class="table-title primary-text" part="label-selected-server">
145
+ SELECTED: ${this.selectedServer?.computedUrl || "none"}
146
+ ${this.mockServer === "true" ? html`<span style="color:var(--green); margin-left:6px;">(Mocked)</span>` : ""}
147
+ </div>
148
+ </div>
149
+ <slot name="servers"></slot>
150
+ ${serverVarsTemplate.call(this)}
151
+ </section>`;
152
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Lit reactive controller behind color-scheme="auto" on <rapi-doc> and <rapi-doc-mini>.
3
+ *
4
+ * "auto" follows the host page: it reads the page's CSS `color-scheme` (as set by Starlight and most
5
+ * theme toggles, e.g. `:root[data-theme='light'] { color-scheme: light }`), or the OS preference when
6
+ * the page declares none or both. The resolved 'dark' / 'light' is written to the element's
7
+ * color-scheme property and attribute, which drive the styles, and is re-resolved when attributes
8
+ * on <html> or <body> change (data-theme, class, style) or the OS preference changes.
9
+ *
10
+ * Setting color-scheme to any other value later turns auto mode off.
11
+ */
12
+ export class AutoColorSchemeController {
13
+ constructor(host) {
14
+ this.host = host;
15
+ this.active = false;
16
+ this.resolved = "";
17
+ this.observer = null;
18
+ this.media = null;
19
+ this.onPageChange = () => this.apply();
20
+ host.addController(this);
21
+ }
22
+
23
+ hostConnected() {
24
+ if (this.host.colorScheme === "auto") {
25
+ this.enable();
26
+ } else if (this.active) {
27
+ this.watch(true);
28
+ this.apply();
29
+ }
30
+ }
31
+
32
+ hostDisconnected() {
33
+ this.watch(false);
34
+ }
35
+
36
+ hostUpdate() {
37
+ const value = this.host.colorScheme;
38
+ if (value === "auto") {
39
+ this.enable();
40
+ } else if (this.active && value !== this.resolved) {
41
+ // An explicit value from the page or a script
42
+ this.active = false;
43
+ this.watch(false);
44
+ }
45
+ }
46
+
47
+ enable() {
48
+ this.active = true;
49
+ this.watch(true);
50
+ this.apply();
51
+ }
52
+
53
+ apply() {
54
+ if (!this.active || typeof window === "undefined") {
55
+ return;
56
+ }
57
+ const scheme = resolvePageColorScheme(this.host);
58
+ this.resolved = scheme;
59
+ if (this.host.colorScheme !== scheme) {
60
+ this.host.colorScheme = scheme;
61
+ }
62
+ // Set the attribute now (property reflection is async) so the styles switch immediately.
63
+ if (this.host.getAttribute("color-scheme") !== scheme) {
64
+ this.host.setAttribute("color-scheme", scheme);
65
+ }
66
+ }
67
+
68
+ watch(enable) {
69
+ this.observer?.disconnect();
70
+ this.observer = null;
71
+ this.media?.removeEventListener("change", this.onPageChange);
72
+ this.media = null;
73
+ if (!enable || typeof window === "undefined") {
74
+ return;
75
+ }
76
+ this.observer = new MutationObserver(this.onPageChange);
77
+ this.observer.observe(document.documentElement, { attributes: true });
78
+ if (document.body) {
79
+ this.observer.observe(document.body, { attributes: true });
80
+ }
81
+ this.media = window.matchMedia("(prefers-color-scheme: light)");
82
+ this.media.addEventListener("change", this.onPageChange);
83
+ }
84
+ }
85
+
86
+ /**
87
+ * 'dark' or 'light' for the page around `el`. Reads the parent's CSS color-scheme: the element sets
88
+ * color-scheme on itself from its own attribute.
89
+ */
90
+ export function resolvePageColorScheme(el) {
91
+ const pageEl = el.parentElement || el.getRootNode().host || document.documentElement;
92
+ const pageScheme = getComputedStyle(pageEl).colorScheme || "";
93
+ const hasDark = /\bdark\b/.test(pageScheme);
94
+ const hasLight = /\blight\b/.test(pageScheme);
95
+ if (hasDark !== hasLight) {
96
+ return hasDark ? "dark" : "light";
97
+ }
98
+ return window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";
99
+ }
@@ -0,0 +1,175 @@
1
+ import {
2
+ expandCollapseNavBarTag,
3
+ expandCollapseNavBarTagGroup,
4
+ expandParentNavContainers,
5
+ } from "../templates/navbar-template.js";
6
+
7
+ /**
8
+ * Connects an external navigation bar (e.g. in Astro, Next.js, or Starlight portals)
9
+ * to a <rapi-doc> component, reusing RapiDoc's internal accordion mechanics,
10
+ * scroll controller, and active item tracking.
11
+ *
12
+ * @param {HTMLElement|string} sidebar - Sidebar element or selector
13
+ * @param {HTMLElement|string} rapiDoc - <rapi-doc> element or selector
14
+ * @param {object} [options]
15
+ * @param {string} [options.activeClass='active'] - Class applied to active navigation item
16
+ * @param {boolean} [options.updateHistory=true] - Whether to sync window history
17
+ * @returns {() => void} Cleanup unbind function
18
+ */
19
+ export function bindNavBar(sidebar, rapiDoc, options = {}) {
20
+ const sidebarEl = typeof sidebar === "string" ? document.querySelector(sidebar) : sidebar;
21
+ const rapiDocEl = typeof rapiDoc === "string" ? document.querySelector(rapiDoc) : rapiDoc;
22
+
23
+ if (!sidebarEl || typeof window === "undefined") {
24
+ return () => {};
25
+ }
26
+
27
+ const activeClass = options.activeClass || "active";
28
+ const updateHistory = options.updateHistory !== false;
29
+
30
+ // 1. Resolve matching navigation link in sidebar
31
+ function findMatchingLink(targetId) {
32
+ if (!targetId) return null;
33
+ return (
34
+ sidebarEl.querySelector(`#link-${CSS.escape(targetId)}`) ||
35
+ sidebarEl.querySelector(`.nav-bar-path[data-content-id="${CSS.escape(targetId)}"]`) ||
36
+ sidebarEl.querySelector(`.nav-bar-path[data-op-slug="${CSS.escape(targetId)}"]`) ||
37
+ sidebarEl.querySelector(`#link-${CSS.escape(targetId.replace(/^[a-z0-9_-]+--/, ""))}`) ||
38
+ (targetId === "overview" ? sidebarEl.querySelector("#link-overview, .nav-bar-info") : null)
39
+ );
40
+ }
41
+
42
+ // 2. Set active navigation link and auto-expand parent containers
43
+ function setActiveNavId(targetId, shouldUpdateHistory = true) {
44
+ const link = findMatchingLink(targetId);
45
+ if (!link || link.classList.contains(activeClass)) return;
46
+
47
+ sidebarEl.querySelectorAll("." + activeClass).forEach((el) => el.classList.remove(activeClass));
48
+ link.classList.add(activeClass);
49
+
50
+ // Reuse RapiDoc's native hierarchy expander
51
+ expandParentNavContainers(link);
52
+
53
+ if (shouldUpdateHistory && updateHistory) {
54
+ const href = link.getAttribute("href");
55
+ if (href) window.history.replaceState(null, "", href);
56
+ }
57
+
58
+ // Scroll active item into view inside the sidebar container
59
+ const navScroll = link.closest(".nav-scroll");
60
+ if (navScroll) {
61
+ const itemRect = link.getBoundingClientRect();
62
+ const scrollRect = navScroll.getBoundingClientRect();
63
+ if (itemRect.top < scrollRect.top + 20 || itemRect.bottom > scrollRect.bottom - 40) {
64
+ const targetTop = itemRect.top - scrollRect.top + navScroll.scrollTop - 40;
65
+ navScroll.scrollTo({ top: targetTop, behavior: "smooth" });
66
+ }
67
+ }
68
+ }
69
+
70
+ // 3. Delegated Click Listener (reusing RapiDoc's accordion handlers)
71
+ const handleClick = (e) => {
72
+ const navEl =
73
+ e.target.closest("[data-action]") || e.target.closest(".nav-bar-path, .nav-bar-info");
74
+ if (!navEl) return;
75
+
76
+ const action = navEl.dataset?.action;
77
+ if (action === "expand-collapse-tag") {
78
+ expandCollapseNavBarTag(navEl, "toggle");
79
+ } else if (action === "expand-collapse-tag-group") {
80
+ expandCollapseNavBarTagGroup(navEl, "toggle");
81
+ } else if (
82
+ action === "navigate" ||
83
+ navEl.classList.contains("nav-bar-path") ||
84
+ navEl.classList.contains("nav-bar-info")
85
+ ) {
86
+ const href = navEl.getAttribute("href") || navEl.getAttribute("data-tag-href");
87
+ const hashIdx = href ? href.indexOf("#") : -1;
88
+ const elementId =
89
+ (hashIdx !== -1 ? href.substring(hashIdx + 1) : href) ||
90
+ navEl.getAttribute("data-content-id") ||
91
+ navEl.getAttribute("data-op-slug") ||
92
+ navEl.id?.replace(/^link-/, "");
93
+
94
+ if (elementId && rapiDocEl && typeof rapiDocEl.scrollToPath === "function") {
95
+ e.preventDefault();
96
+ setActiveNavId(elementId, false);
97
+ rapiDocEl.scrollToPath(elementId);
98
+ if (updateHistory && href) {
99
+ window.history.pushState(null, "", href);
100
+ }
101
+ }
102
+ }
103
+ };
104
+ sidebarEl.addEventListener("click", handleClick);
105
+
106
+ // 4. Sidebar Search Filter
107
+ const filterInput = sidebarEl.querySelector(".portal-filter-input, .nav-bar-search-input");
108
+ let handleFilter = null;
109
+ if (filterInput) {
110
+ handleFilter = (e) => {
111
+ const query = (e.target.value || "").toLowerCase().trim();
112
+ sidebarEl.querySelectorAll(".nav-bar-path, .portal-sidebar-op-item").forEach((item) => {
113
+ item.style.display = item.textContent.toLowerCase().includes(query) ? "flex" : "none";
114
+ });
115
+ sidebarEl.querySelectorAll(".nav-bar-tag-and-paths").forEach((tag) => {
116
+ const hasVisible = tag.querySelector(
117
+ '.nav-bar-path:not([style*="display: none"]), .portal-sidebar-op-item:not([style*="display: none"])',
118
+ );
119
+ tag.style.display = hasVisible || !query ? "" : "none";
120
+ if (query && hasVisible) expandCollapseNavBarTag(tag, "expand");
121
+ });
122
+ sidebarEl.querySelectorAll(".nav-bar-tag-group").forEach((group) => {
123
+ const hasVisible = group.querySelector(
124
+ '.nav-bar-path:not([style*="display: none"]), .portal-sidebar-op-item:not([style*="display: none"])',
125
+ );
126
+ group.style.display = hasVisible || !query ? "" : "none";
127
+ if (query && hasVisible) expandCollapseNavBarTagGroup(group, "expand");
128
+ });
129
+ };
130
+ filterInput.addEventListener("input", handleFilter);
131
+ }
132
+
133
+ // 5. Native rapi-doc nav-active-change event listener
134
+ const handleNavActiveChange = (e) => {
135
+ const targetId = e.detail?.operationId || e.detail?.id || e.detail?.elementId;
136
+ if (targetId) setActiveNavId(targetId, true);
137
+ };
138
+ if (rapiDocEl) {
139
+ rapiDocEl.addEventListener("nav-active-change", handleNavActiveChange);
140
+ }
141
+
142
+ // 6. Popstate / hash sync
143
+ const handleHashSync = () => {
144
+ if (window.location.hash) {
145
+ const hash = window.location.hash.replace(/^#/, "");
146
+ if (hash) {
147
+ setActiveNavId(hash, false);
148
+ if (rapiDocEl && typeof rapiDocEl.scrollToPath === "function") {
149
+ rapiDocEl.scrollToPath(hash);
150
+ }
151
+ }
152
+ }
153
+ };
154
+ window.addEventListener("popstate", handleHashSync);
155
+ window.addEventListener("hashchange", handleHashSync);
156
+
157
+ // Initial sync on load and spec load
158
+ if (rapiDocEl) {
159
+ rapiDocEl.addEventListener("spec-loaded", () => setTimeout(handleHashSync, 80));
160
+ if (typeof rapiDocEl.updateComplete?.then === "function") {
161
+ rapiDocEl.updateComplete.then(() => setTimeout(handleHashSync, 50));
162
+ }
163
+ }
164
+ setTimeout(handleHashSync, 150);
165
+
166
+ return () => {
167
+ sidebarEl.removeEventListener("click", handleClick);
168
+ if (filterInput && handleFilter) filterInput.removeEventListener("input", handleFilter);
169
+ if (rapiDocEl) rapiDocEl.removeEventListener("nav-active-change", handleNavActiveChange);
170
+ window.removeEventListener("popstate", handleHashSync);
171
+ window.removeEventListener("hashchange", handleHashSync);
172
+ };
173
+ }
174
+
175
+ export default bindNavBar;
@@ -0,0 +1,59 @@
1
+ export default {
2
+ color: {
3
+ inputReverseFg: "#fff",
4
+ inputReverseBg: "#333",
5
+ headerBg: "#444",
6
+ getRgb(hex) {
7
+ if (hex.indexOf("#") === 0) {
8
+ hex = hex.slice(1, 7);
9
+ }
10
+ // convert 3-digit hex to 6-digits.
11
+ if (hex.length === 3 || hex.length === 4) {
12
+ hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
13
+ }
14
+ if (hex.length !== 6) {
15
+ throw new Error("Invalid HEX color.");
16
+ }
17
+ return {
18
+ r: parseInt(hex.slice(0, 2), 16),
19
+ g: parseInt(hex.slice(2, 4), 16),
20
+ b: parseInt(hex.slice(4, 6), 16),
21
+ };
22
+ },
23
+ luminanace(hexColorCode) {
24
+ const rgb = this.getRgb(hexColorCode);
25
+ return rgb.r * 0.299 + rgb.g * 0.587 + rgb.b * 0.114;
26
+ },
27
+ invert(hexColorCode) {
28
+ return this.luminanace(hexColorCode) > 135 ? "#000" : "#fff"; // compare with `>=128`, but giving little more preference to white over black
29
+ },
30
+ opacity(hex, opacity) {
31
+ const rgb = this.getRgb(hex);
32
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${opacity})`;
33
+ },
34
+ brightness(hex, amt) {
35
+ const rgb = this.getRgb(hex);
36
+ rgb.r += amt;
37
+ rgb.g += amt;
38
+ rgb.b += amt;
39
+ if (rgb.r > 255) rgb.r = 255;
40
+ else if (rgb.r < 0) rgb.r = 0;
41
+
42
+ if (rgb.g > 255) rgb.g = 255;
43
+ else if (rgb.g < 0) rgb.g = 0;
44
+
45
+ if (rgb.b > 255) rgb.b = 255;
46
+ else if (rgb.b < 0) rgb.b = 0;
47
+ return `#${rgb.r.toString(16).padStart(2, "0")}${rgb.g.toString(16).padStart(2, "0")}${rgb.b.toString(16).padStart(2, "0")}`;
48
+ },
49
+ hasGoodContrast(hexColorCode1, hexColorCode2) {
50
+ const lum1 = this.luminanace(hexColorCode1);
51
+ const lum2 = this.luminanace(hexColorCode2);
52
+ return lum1 - lum2;
53
+ },
54
+ },
55
+ };
56
+
57
+ export function isValidHexColor(colorCode) {
58
+ return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}|[A-Fa-f0-9]{8}|[A-Fa-f0-9]{4})$/i.test(colorCode);
59
+ }
@@ -0,0 +1,191 @@
1
+ /* For Delayed Event Handler Execution */
2
+ export function debounce(fn, delay) {
3
+ let timeoutID = null;
4
+ return (...args) => {
5
+ clearTimeout(timeoutID);
6
+ const that = this;
7
+ timeoutID = setTimeout(() => {
8
+ fn.apply(that, args);
9
+ }, delay);
10
+ };
11
+ }
12
+
13
+ export const invalidCharsRegEx = /[\s#:?&={}]/g; // used for generating valid html element ids by replacing the invalid chars with hyphen (-)
14
+ export const rapidocApiKey = "_rapidoc_api_key";
15
+
16
+ export function sleep(ms) {
17
+ return new Promise((resolve) => setTimeout(resolve, ms));
18
+ }
19
+
20
+ export function copyToClipboard(data, e) {
21
+ const btnEl = e.target;
22
+ const textArea = document.createElement("textarea");
23
+ textArea.value = data;
24
+ textArea.style.position = "fixed"; // avoid scrolling to bottom
25
+ document.body.appendChild(textArea);
26
+ textArea.focus();
27
+ textArea.select();
28
+ try {
29
+ document.execCommand("copy");
30
+ btnEl.innerText = "Copied";
31
+ setTimeout(() => {
32
+ btnEl.innerText = "Copy";
33
+ }, 5000);
34
+ } catch (err) {
35
+ console.error("Unable to copy", err);
36
+ }
37
+ document.body.removeChild(textArea);
38
+ }
39
+
40
+ export function getBaseUrlFromUrl(url) {
41
+ const pathArray = url.split("/");
42
+ return `${pathArray[0]}//${pathArray[2]}`;
43
+ }
44
+
45
+ export async function wait(ms) {
46
+ return new Promise((resolve) => {
47
+ setTimeout(resolve, ms);
48
+ });
49
+ }
50
+
51
+ export function getMatchedComponents(searchVal, component) {
52
+ return component.name.toLowerCase().includes(searchVal.toLowerCase());
53
+ }
54
+
55
+ export function getMatchedPaths(searchVal = "", path, tagName = "") {
56
+ if (!searchVal) {
57
+ return true;
58
+ }
59
+ const stringToSearch =
60
+ `${path.method} ${path.path} ${path.summary || ""} ${path.description || ""} ${path.operationId || ""} ${tagName}`.toLowerCase();
61
+ return stringToSearch.includes(searchVal.toLowerCase());
62
+ }
63
+
64
+ export function isInternalOrHidden(item) {
65
+ if (!item || typeof item !== "object") return false;
66
+ return (
67
+ item.xInternal === true ||
68
+ item.xInternal === "true" ||
69
+ item.xHidden === true ||
70
+ item.xHidden === "true" ||
71
+ item["x-internal"] === true ||
72
+ item["x-internal"] === "true" ||
73
+ item["x-hidden"] === true ||
74
+ item["x-hidden"] === "true" ||
75
+ item.rawOperation?.["x-internal"] === true ||
76
+ item.rawOperation?.["x-internal"] === "true" ||
77
+ item.rawOperation?.["x-hidden"] === true ||
78
+ item.rawOperation?.["x-hidden"] === "true"
79
+ );
80
+ }
81
+
82
+ export function schemaKeys(schemaProps, result = new Set()) {
83
+ if (!schemaProps) {
84
+ return result;
85
+ }
86
+ Object.keys(schemaProps).forEach((key) => {
87
+ result.add(key);
88
+ if (schemaProps[key].properties) {
89
+ schemaKeys(schemaProps[key].properties, result);
90
+ } else if (schemaProps[key].items?.properties) {
91
+ schemaKeys(schemaProps[key].items?.properties, result);
92
+ }
93
+ });
94
+ return result;
95
+ }
96
+
97
+ export function advancedSearch(searchVal, allSpecTags, searchOptions = []) {
98
+ if (!searchVal.trim() || searchOptions.length === 0) {
99
+ return;
100
+ }
101
+
102
+ const pathsMatched = [];
103
+ allSpecTags.forEach((tag) => {
104
+ tag.paths.forEach((path) => {
105
+ let stringToSearch = "";
106
+ if (searchOptions.includes("search-api-path")) {
107
+ stringToSearch = path.path;
108
+ }
109
+ if (searchOptions.includes("search-api-descr")) {
110
+ stringToSearch = `${stringToSearch} ${(path.summary || "") + (path.description || "")}`;
111
+ }
112
+ if (searchOptions.includes("search-api-params")) {
113
+ stringToSearch = `${stringToSearch} ${path.parameters?.map((v) => v.name).join(" ") || ""}`;
114
+ }
115
+
116
+ if (searchOptions.includes("search-api-request-body") && path.requestBody) {
117
+ let schemaKeySet = new Set();
118
+ for (const contentType in path.requestBody?.content) {
119
+ if (path.requestBody.content[contentType].schema?.properties) {
120
+ schemaKeySet = schemaKeys(path.requestBody.content[contentType].schema?.properties);
121
+ }
122
+ stringToSearch = `${stringToSearch} ${[...schemaKeySet].join(" ")}`;
123
+ }
124
+ }
125
+
126
+ if (searchOptions.includes("search-api-resp-descr")) {
127
+ stringToSearch = `${stringToSearch} ${Object.values(path.responses)
128
+ .map((v) => v.description || "")
129
+ .join(" ")}`;
130
+ }
131
+
132
+ if (stringToSearch.toLowerCase().includes(searchVal.trim().toLowerCase())) {
133
+ pathsMatched.push({
134
+ elementId: path.elementId,
135
+ method: path.method,
136
+ path: path.path,
137
+ summary: path.summary || path.description || "",
138
+ deprecated: path.deprecated,
139
+ });
140
+ }
141
+ });
142
+ });
143
+ return pathsMatched;
144
+ }
145
+
146
+ /*
147
+ export function prettyXml(sourceXmlString) {
148
+ const xmlDoc = new DOMParser().parseFromString(sourceXmlString, 'text/xml');
149
+ const xsltDoc = new DOMParser().parseFromString([
150
+ // describes how we want to modify the XML - indent everything
151
+ `<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
152
+ <xsl:strip-space elements="*"/>
153
+ <xsl:template match="para[content-style][not(text())]">
154
+ <xsl:value-of select="normalize-space(.)"/>
155
+ </xsl:template>
156
+ <xsl:template match="node()|@*">
157
+ <xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy>
158
+ </xsl:template>
159
+ <xsl:output indent="yes"/>
160
+ </xsl:stylesheet>`,
161
+ ].join('\n'), 'application/xml');
162
+ const xsltProcessor = new XSLTProcessor();
163
+ xsltProcessor.importStylesheet(xsltDoc);
164
+ const resultDoc = xsltProcessor.transformToDocument(xmlDoc);
165
+ return new XMLSerializer().serializeToString(resultDoc);
166
+ }
167
+ */
168
+
169
+ export function downloadResource(url, fileName) {
170
+ if (url) {
171
+ const a = document.createElement("a");
172
+ document.body.appendChild(a);
173
+ a.style = "display: none";
174
+ a.href = url;
175
+ a.download = fileName;
176
+ a.click();
177
+ a.remove();
178
+ }
179
+ }
180
+
181
+ export function viewResource(url) {
182
+ if (url) {
183
+ const a = document.createElement("a");
184
+ document.body.appendChild(a);
185
+ a.style = "display: none";
186
+ a.href = url;
187
+ a.target = "_blank";
188
+ a.click();
189
+ a.remove();
190
+ }
191
+ }