rapidoc 12.0.0 → 12.1.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.
- package/README.md +127 -25
- package/dist/rapidoc-flow-min.js +580 -0
- package/dist/rapidoc-flow-min.js.LICENSE.txt +171 -0
- package/dist/rapidoc-min.js +541 -1309
- package/dist/rapidoc-min.js.LICENSE.txt +381 -0
- package/package.json +21 -18
- package/src/README.md +150 -0
- package/src/components/README.md +91 -0
- package/src/components/api-request.ts +43 -0
- package/src/components/api-response.ts +9 -4
- package/src/components/schema-base-element.ts +18 -4
- package/src/components/schema-table.ts +58 -13
- package/src/components/schema-tree.ts +7 -0
- package/src/components/tag-input.ts +5 -9
- package/src/flow.js +3 -0
- package/src/rapi-flow.ts +1178 -0
- package/src/rapidoc-mini.ts +27 -6
- package/src/rapidoc.ts +108 -62
- package/src/styles/README.md +96 -0
- package/src/styles/css-bundle.js +2 -2
- package/src/styles/endpoint-styles.js +6 -1
- package/src/styles/flow-styles.js +248 -0
- package/src/styles/nav-styles.js +39 -15
- package/src/styles/schema-styles.js +13 -5
- package/src/styles/theme-tokens.js +2 -2
- package/src/templates/README.md +89 -0
- package/src/templates/api-response-template.js +14 -10
- package/src/templates/callback-template.js +1 -1
- package/src/templates/downloads-template.js +12 -27
- package/src/templates/endpoint-template.js +15 -24
- package/src/templates/expanded-endpoint-template.js +8 -4
- package/src/templates/focused-endpoint-template.js +4 -3
- package/src/templates/navbar-template.js +20 -11
- package/src/templates/request-body-template.js +29 -7
- package/src/templates/request-params-template.js +15 -4
- package/src/templates/security-scheme-template.js +11 -2
- package/src/templates/server-template.js +3 -2
- package/src/utils/README.md +146 -0
- package/src/utils/arazzo/README.md +119 -0
- package/src/utils/arazzo/components.js +15 -0
- package/src/utils/arazzo/graph.js +269 -0
- package/src/utils/arazzo/index.js +52 -0
- package/src/utils/arazzo/operations.js +106 -0
- package/src/utils/arazzo/parse.js +196 -0
- package/src/utils/arazzo/refs.js +40 -0
- package/src/utils/arazzo/svg.js +707 -0
- package/src/utils/curl-utils.js +5 -2
- package/src/utils/highlighter.js +3 -0
- package/src/utils/mcp/README.md +155 -0
- package/src/utils/mcp/common.js +45 -346
- package/src/utils/mcp/manifest.js +382 -0
- package/src/utils/mcp/project/server.ts +53 -0
- package/src/utils/mcp/project.js +88 -0
- package/src/utils/mcp/runtime.js +289 -0
- package/src/utils/mcp/schema.js +98 -0
- package/src/utils/mcp/workflow-runtime.js +374 -0
- package/src/utils/mcp/workflows.js +240 -0
- package/src/utils/mcp/zip.js +102 -0
- package/src/utils/mcp-generator.js +29 -125
- package/src/utils/mock-interceptor.js +134 -85
- package/src/utils/request-utils.js +61 -0
- package/src/utils/schema-utils.js +33 -6
- package/src/utils/slugger.js +5 -7
- package/src/utils/spec-parser.js +190 -164
- package/src/utils/url-utils.js +36 -1
- package/dist/rapidoc-static-min.js +0 -13542
- package/src/static/index.d.ts +0 -149
- package/src/static/index.js +0 -62
- package/src/static/navigation.js +0 -497
- package/src/static/render.js +0 -490
- package/src/static/serializer.js +0 -216
- package/src/utils/mcp/generators/csharp.js +0 -242
- package/src/utils/mcp/generators/go.js +0 -298
- package/src/utils/mcp/generators/java.js +0 -330
- package/src/utils/mcp/generators/python.js +0 -248
- package/src/utils/mcp/generators/rust.js +0 -281
- package/src/utils/mcp/generators/typescript.js +0 -290
package/src/static/navigation.js
DELETED
|
@@ -1,497 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Direct OpenAPI Navigation HTML Serializer & Metadata Extractor.
|
|
3
|
-
*
|
|
4
|
-
* Directly renders framework-agnostic navigation HTML from OpenAPI specifications,
|
|
5
|
-
* eliminating intermediate AST node abstractions:
|
|
6
|
-
* - `getNavBarHtml(specInput, options)`: Resolves spec and renders navigation HTML directly
|
|
7
|
-
* - `renderNavBarHtml(resolvedSpec, options)`: Pure zero-dependency HTML generator
|
|
8
|
-
* - `getSpecNavigation(specInput, options)`: Extracts semantic navigation metadata (tagGroups & tags)
|
|
9
|
-
*
|
|
10
|
-
* Supports 3-tier navigation (x-tagGroups / x-tag-groups) with fallback to 2-tier tags.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { resolveSpecInput } from "./render.js";
|
|
14
|
-
import { escapeHtml } from "./serializer.js";
|
|
15
|
-
import { OperationSlugger, toKebabCase } from "../utils/slugger.js";
|
|
16
|
-
import { bindNavBar } from "../utils/bind-navbar.js";
|
|
17
|
-
import { isInternalOrHidden } from "../utils/common-utils.js";
|
|
18
|
-
|
|
19
|
-
export { isInternalOrHidden };
|
|
20
|
-
|
|
21
|
-
/** Values of show-method-in-nav-bar (rapidoc.ts accepts the same). */
|
|
22
|
-
const METHOD_STYLES = ["false", "as-plain-text", "as-colored-text", "as-colored-block"];
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Normalizes HTTP method badge string (3-character format matching core RapiDoc).
|
|
26
|
-
* e.g., 'delete' -> 'DEL', 'post' -> 'POS', 'get' -> 'GET', 'put' -> 'PUT'
|
|
27
|
-
*
|
|
28
|
-
* @param {string} method
|
|
29
|
-
* @param {'3-char'|'full'} [format='3-char']
|
|
30
|
-
* @returns {string}
|
|
31
|
-
*/
|
|
32
|
-
export function getMethodBadge(method, format = "3-char") {
|
|
33
|
-
if (!method) return "";
|
|
34
|
-
const m = String(method).toLowerCase();
|
|
35
|
-
if (format === "3-char") {
|
|
36
|
-
return m.substring(0, 3).toUpperCase();
|
|
37
|
-
}
|
|
38
|
-
return m.toUpperCase();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Retrieves visible operations for a tag, supporting both parsed and raw specs.
|
|
43
|
-
*
|
|
44
|
-
* @param {Object} tag
|
|
45
|
-
* @param {Object} spec
|
|
46
|
-
* @param {OperationSlugger} [sharedSlugger]
|
|
47
|
-
*/
|
|
48
|
-
function getTagOperations(tag, spec, sharedSlugger) {
|
|
49
|
-
if (Array.isArray(tag.paths) && tag.paths.length > 0) {
|
|
50
|
-
return tag.paths.filter((p) => !isInternalOrHidden(p));
|
|
51
|
-
}
|
|
52
|
-
if (!spec?.paths || typeof spec.paths !== "object") return [];
|
|
53
|
-
|
|
54
|
-
const tagName = (tag.name || "").toLowerCase();
|
|
55
|
-
const operations = [];
|
|
56
|
-
const httpMethods = ["get", "post", "put", "delete", "patch", "options", "head", "trace"];
|
|
57
|
-
const slugger = sharedSlugger || new OperationSlugger();
|
|
58
|
-
const tagSlug = toKebabCase(tag.name);
|
|
59
|
-
|
|
60
|
-
for (const [pathKey, pathObj] of Object.entries(spec.paths)) {
|
|
61
|
-
if (!pathObj || typeof pathObj !== "object") continue;
|
|
62
|
-
for (const method of httpMethods) {
|
|
63
|
-
const op = pathObj[method];
|
|
64
|
-
if (op && typeof op === "object" && !isInternalOrHidden(op)) {
|
|
65
|
-
const opTags = Array.isArray(op.tags) ? op.tags.map((t) => String(t).toLowerCase()) : [];
|
|
66
|
-
if (opTags.includes(tagName)) {
|
|
67
|
-
operations.push({
|
|
68
|
-
method,
|
|
69
|
-
path: pathKey,
|
|
70
|
-
summary: op.summary || "",
|
|
71
|
-
description: op.description || "",
|
|
72
|
-
deprecated: Boolean(op.deprecated),
|
|
73
|
-
operationId: op.operationId || "",
|
|
74
|
-
elementId: slugger.slug(
|
|
75
|
-
{
|
|
76
|
-
method,
|
|
77
|
-
path: pathKey,
|
|
78
|
-
operationId: op.operationId,
|
|
79
|
-
summary: op.summary || op.description,
|
|
80
|
-
},
|
|
81
|
-
tagSlug,
|
|
82
|
-
),
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return operations;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Extracts a complete, framework-agnostic navigation hierarchy from an OpenAPI spec.
|
|
93
|
-
*
|
|
94
|
-
* @param {string|Object} specInput - Specification file path, URL, raw string, or parsed spec
|
|
95
|
-
* @param {Object} [options]
|
|
96
|
-
* @returns {Promise<{ title: string, version: string, description: string, tagGroups: Array, tags: Array, remainingTags: Array }>}
|
|
97
|
-
*/
|
|
98
|
-
export async function getSpecNavigation(specInput, options = {}) {
|
|
99
|
-
const resolvedSpec = await resolveSpecInput(specInput, options);
|
|
100
|
-
const slugger = new OperationSlugger();
|
|
101
|
-
|
|
102
|
-
const allTags = (resolvedSpec.tags || [])
|
|
103
|
-
.filter((t) => !isInternalOrHidden(t))
|
|
104
|
-
.map((t) => {
|
|
105
|
-
const paths = getTagOperations(t, resolvedSpec, slugger);
|
|
106
|
-
return {
|
|
107
|
-
name: t.name || t.displayName || "",
|
|
108
|
-
displayName: t.displayName || t.name || "",
|
|
109
|
-
description: t.description || "",
|
|
110
|
-
elementId: t.elementId || slugger.slugTag(t.name),
|
|
111
|
-
expanded: t.expanded !== false,
|
|
112
|
-
paths,
|
|
113
|
-
};
|
|
114
|
-
})
|
|
115
|
-
.filter((t) => t.paths.length > 0);
|
|
116
|
-
|
|
117
|
-
const assignedTagNames = new Set();
|
|
118
|
-
const tagGroups = (resolvedSpec.tagGroups || [])
|
|
119
|
-
.filter((group) => !isInternalOrHidden(group))
|
|
120
|
-
.map((group) => {
|
|
121
|
-
const groupTags = (group.tags || [])
|
|
122
|
-
.filter((t) => !isInternalOrHidden(t))
|
|
123
|
-
.map((t) => {
|
|
124
|
-
assignedTagNames.add((t.name || "").toLowerCase());
|
|
125
|
-
const paths = getTagOperations(t, resolvedSpec, slugger);
|
|
126
|
-
return {
|
|
127
|
-
name: t.name || t.displayName || "",
|
|
128
|
-
displayName: t.displayName || t.name || "",
|
|
129
|
-
description: t.description || "",
|
|
130
|
-
elementId: t.elementId || slugger.slugTag(t.name),
|
|
131
|
-
expanded: t.expanded !== false,
|
|
132
|
-
paths,
|
|
133
|
-
};
|
|
134
|
-
})
|
|
135
|
-
.filter((t) => t.paths.length > 0);
|
|
136
|
-
|
|
137
|
-
if (groupTags.length === 0) return null;
|
|
138
|
-
return {
|
|
139
|
-
name: group.name || group.displayName || "",
|
|
140
|
-
displayName: group.displayName || group.name || "",
|
|
141
|
-
description: group.description || "",
|
|
142
|
-
elementId: group.elementId || slugger.slugTagGroup(group.name),
|
|
143
|
-
expanded: group.expanded !== false,
|
|
144
|
-
tags: groupTags,
|
|
145
|
-
};
|
|
146
|
-
})
|
|
147
|
-
.filter(Boolean);
|
|
148
|
-
|
|
149
|
-
const remainingTags =
|
|
150
|
-
tagGroups.length > 0 ? allTags.filter((t) => !assignedTagNames.has(t.name.toLowerCase())) : [];
|
|
151
|
-
|
|
152
|
-
return {
|
|
153
|
-
title: resolvedSpec.info?.title || "",
|
|
154
|
-
version: resolvedSpec.info?.version || "",
|
|
155
|
-
description: resolvedSpec.info?.description || "",
|
|
156
|
-
tagGroups,
|
|
157
|
-
tags: allTags,
|
|
158
|
-
remainingTags,
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Renders a single operation item link.
|
|
164
|
-
*/
|
|
165
|
-
function renderOperation(op, tagSlug, ctx) {
|
|
166
|
-
if (!op || isInternalOrHidden(op)) return "";
|
|
167
|
-
const method = op.method ? op.method.toLowerCase() : "get";
|
|
168
|
-
const path = op.path || "";
|
|
169
|
-
|
|
170
|
-
// Extract clean operation slug without tag prefix
|
|
171
|
-
const rawElementId = op.elementId || ctx.slugger.slug(op, tagSlug);
|
|
172
|
-
let opSlug;
|
|
173
|
-
let hierarchicalId;
|
|
174
|
-
|
|
175
|
-
if (op.elementId) {
|
|
176
|
-
hierarchicalId = op.elementId;
|
|
177
|
-
if (tagSlug && tagSlug !== "general") {
|
|
178
|
-
const prefix = `${tagSlug}--`;
|
|
179
|
-
opSlug = hierarchicalId.startsWith(prefix)
|
|
180
|
-
? hierarchicalId.slice(prefix.length)
|
|
181
|
-
: ctx.slugger.slugOp
|
|
182
|
-
? ctx.slugger.slugOp(op)
|
|
183
|
-
: hierarchicalId;
|
|
184
|
-
} else {
|
|
185
|
-
opSlug = hierarchicalId;
|
|
186
|
-
}
|
|
187
|
-
} else {
|
|
188
|
-
opSlug = ctx.slugger.slugOp
|
|
189
|
-
? ctx.slugger.slugOp(op)
|
|
190
|
-
: rawElementId.replace(/^[a-z0-9_-]+--/, "");
|
|
191
|
-
hierarchicalId = tagSlug && tagSlug !== "general" ? `${tagSlug}--${opSlug}` : opSlug;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
let href;
|
|
195
|
-
let contentId;
|
|
196
|
-
if (ctx.routingMode === "tag-pages" && tagSlug && tagSlug !== "general") {
|
|
197
|
-
href = `${ctx.cleanBase}/${tagSlug}#${opSlug}`;
|
|
198
|
-
contentId = opSlug;
|
|
199
|
-
} else {
|
|
200
|
-
href = `${ctx.cleanBase}#${hierarchicalId}`;
|
|
201
|
-
contentId = hierarchicalId;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const label = ctx.usePathInNav
|
|
205
|
-
? `<span class="mono-font">${escapeHtml(path)}</span>`
|
|
206
|
-
: escapeHtml(op.summary || op.shortSummary || `${method.toUpperCase()} ${path}`);
|
|
207
|
-
// As in <rapi-doc>: a three-letter badge for as-colored-block, the full method otherwise, nothing for false.
|
|
208
|
-
const methodStyle = ctx.showMethodInNavBar;
|
|
209
|
-
const methodBadge = getMethodBadge(
|
|
210
|
-
method,
|
|
211
|
-
methodStyle === "as-colored-block" ? ctx.methodBadgeFormat : "full",
|
|
212
|
-
);
|
|
213
|
-
const methodHtml =
|
|
214
|
-
methodStyle === "false"
|
|
215
|
-
? ""
|
|
216
|
-
: `\n <span class="nav-method ${methodStyle} ${method}">${methodBadge}</span>`;
|
|
217
|
-
const isActive = Boolean(
|
|
218
|
-
ctx.activeHref &&
|
|
219
|
-
(ctx.activeHref === href ||
|
|
220
|
-
ctx.activeHref.endsWith(`#${contentId}`) ||
|
|
221
|
-
(opSlug && ctx.activeHref.endsWith(`#${opSlug}`))),
|
|
222
|
-
);
|
|
223
|
-
const deprecatedClass = op.deprecated ? " deprecated" : "";
|
|
224
|
-
const extraClass = ctx.itemClassName ? ` ${ctx.itemClassName}` : "";
|
|
225
|
-
|
|
226
|
-
return `
|
|
227
|
-
<a
|
|
228
|
-
href="${escapeHtml(href)}"
|
|
229
|
-
class="nav-bar-path${extraClass} ${isActive ? "active" : ""}${deprecatedClass}"
|
|
230
|
-
part="section-navbar-item section-navbar-path"
|
|
231
|
-
data-action="navigate"
|
|
232
|
-
data-content-id="${contentId}"
|
|
233
|
-
data-op-slug="${opSlug}"
|
|
234
|
-
data-path="${escapeHtml(path)}"
|
|
235
|
-
id="link-${contentId}"
|
|
236
|
-
tabindex="0"
|
|
237
|
-
>
|
|
238
|
-
<span class="nav-bar-path-label">${label}</span>${methodHtml}
|
|
239
|
-
</a>`;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Renders a tag accordion section and its underlying operations.
|
|
244
|
-
*/
|
|
245
|
-
function renderTag(tag, ctx) {
|
|
246
|
-
if (!tag || isInternalOrHidden(tag)) return "";
|
|
247
|
-
const visiblePaths = getTagOperations(tag, ctx.resolvedSpec, ctx.slugger);
|
|
248
|
-
if (visiblePaths.length === 0) return "";
|
|
249
|
-
const tagSlug = toKebabCase(tag.name);
|
|
250
|
-
const elementId = tag.elementId || ctx.slugger.slugTag(tag.name);
|
|
251
|
-
const title = tag.displayName || tag.name || "";
|
|
252
|
-
const isExpanded = tag.expanded !== false;
|
|
253
|
-
const pathsHtml = visiblePaths.map((op) => renderOperation(op, tagSlug, ctx)).join("");
|
|
254
|
-
const tagHref =
|
|
255
|
-
ctx.routingMode === "tag-pages" && tagSlug && tagSlug !== "general"
|
|
256
|
-
? `${ctx.cleanBase}/${tagSlug}`
|
|
257
|
-
: `${ctx.basePath}#${elementId}`;
|
|
258
|
-
|
|
259
|
-
return `
|
|
260
|
-
<div class="nav-bar-tag-and-paths ${isExpanded ? "expanded" : "collapsed"}" id="link-${elementId}">
|
|
261
|
-
<div
|
|
262
|
-
class="nav-bar-tag"
|
|
263
|
-
part="section-navbar-item section-navbar-tag"
|
|
264
|
-
id="link-${elementId}-header"
|
|
265
|
-
data-action="expand-collapse-tag"
|
|
266
|
-
data-tag-href="${escapeHtml(tagHref)}"
|
|
267
|
-
tabindex="0"
|
|
268
|
-
role="button"
|
|
269
|
-
aria-expanded="${isExpanded ? "true" : "false"}"
|
|
270
|
-
>
|
|
271
|
-
<div class="nav-bar-tag-title" data-tag-href="${escapeHtml(tagHref)}">${escapeHtml(title)}</div>
|
|
272
|
-
<div class="nav-bar-tag-icon" tabindex="0" data-action="expand-collapse-tag"></div>
|
|
273
|
-
</div>
|
|
274
|
-
<div class="nav-bar-paths-under-tag">
|
|
275
|
-
${pathsHtml}
|
|
276
|
-
</div>
|
|
277
|
-
</div>`;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* Renders a 3-tier tag group accordion.
|
|
282
|
-
*/
|
|
283
|
-
function renderGroup(group, ctx) {
|
|
284
|
-
if (!group || isInternalOrHidden(group)) return "";
|
|
285
|
-
const groupTagsHtml = (group.tags || [])
|
|
286
|
-
.map((t) => renderTag(t, ctx))
|
|
287
|
-
.filter(Boolean)
|
|
288
|
-
.join("");
|
|
289
|
-
if (!groupTagsHtml) return "";
|
|
290
|
-
const groupId = group.elementId || ctx.slugger.slugTagGroup(group.name);
|
|
291
|
-
const title = group.displayName || group.name || "";
|
|
292
|
-
const isExpanded = group.expanded !== false;
|
|
293
|
-
|
|
294
|
-
return `
|
|
295
|
-
<div class="nav-bar-tag-group ${isExpanded ? "expanded" : "collapsed"}" id="link-${groupId}">
|
|
296
|
-
<div
|
|
297
|
-
class="nav-bar-tag-group-header"
|
|
298
|
-
part="section-navbar-item section-navbar-tag-group"
|
|
299
|
-
data-action="expand-collapse-tag-group"
|
|
300
|
-
tabindex="0"
|
|
301
|
-
role="button"
|
|
302
|
-
aria-expanded="${isExpanded ? "true" : "false"}"
|
|
303
|
-
>
|
|
304
|
-
<div class="nav-bar-tag-group-title">${escapeHtml(title)}</div>
|
|
305
|
-
<div class="nav-bar-tag-group-icon" tabindex="0" data-action="expand-collapse-tag-group"></div>
|
|
306
|
-
</div>
|
|
307
|
-
<div class="nav-bar-tags-under-group">
|
|
308
|
-
${groupTagsHtml}
|
|
309
|
-
</div>
|
|
310
|
-
</div>`;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Renders standard RapiDoc navigation filter/search header HTML.
|
|
315
|
-
*
|
|
316
|
-
* @param {Object} [options]
|
|
317
|
-
* @param {string} [options.searchPlaceholder='Filter...']
|
|
318
|
-
* @param {string} [options.inputClassName='']
|
|
319
|
-
* @returns {string}
|
|
320
|
-
*/
|
|
321
|
-
export function getNavFilterHtml(options = {}) {
|
|
322
|
-
const placeholder = options.searchPlaceholder || options.placeholder || "Filter...";
|
|
323
|
-
const extraClass = options.inputClassName ? ` ${options.inputClassName}` : "";
|
|
324
|
-
return `
|
|
325
|
-
<div class="nav-bar-header" part="section-navbar-header">
|
|
326
|
-
<div class="nav-bar-search-container" part="section-navbar-search">
|
|
327
|
-
<div class="nav-bar-search-input-wrapper">
|
|
328
|
-
<input
|
|
329
|
-
id="nav-bar-search"
|
|
330
|
-
part="textbox textbox-nav-filter"
|
|
331
|
-
class="nav-bar-search-input portal-filter-input${extraClass}"
|
|
332
|
-
type="text"
|
|
333
|
-
placeholder="${escapeHtml(placeholder)}"
|
|
334
|
-
aria-label="Filter endpoints"
|
|
335
|
-
spellcheck="false"
|
|
336
|
-
/>
|
|
337
|
-
<svg class="nav-bar-search-icon" viewBox="0 0 256 256" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M232.49,215.51,185,168a92.12,92.12,0,1,0-17,17l47.53,47.54a12,12,0,0,0,17-17ZM44,112a68,68,0,1,1,68,68A68.07,68.07,0,0,1,44,112Z"/></svg>
|
|
338
|
-
</div>
|
|
339
|
-
</div>
|
|
340
|
-
</div>`;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* Asynchronously generates complete navigation HTML directly from a spec input (file, URL, or object).
|
|
345
|
-
*
|
|
346
|
-
* @param {string|Object} specInput
|
|
347
|
-
* @param {Object} [options]
|
|
348
|
-
* @returns {Promise<string>}
|
|
349
|
-
*/
|
|
350
|
-
export async function getNavBarHtml(specInput, options = {}) {
|
|
351
|
-
const resolvedSpec = await resolveSpecInput(specInput, options);
|
|
352
|
-
return renderNavBarHtml(resolvedSpec, options);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* Pure, zero-dependency HTML string generator for OpenAPI navigation.
|
|
357
|
-
* Produces standard RapiDoc navigation HTML directly from the spec.
|
|
358
|
-
*
|
|
359
|
-
* @param {Object} resolvedSpec - Resolved OpenAPI specification
|
|
360
|
-
* @param {Object} [options]
|
|
361
|
-
* @param {string} [options.basePath=''] - Base URL prefix for all links (e.g. '/apis/shipments')
|
|
362
|
-
* @param {string} [options.activeHref=''] - Currently active path or anchor
|
|
363
|
-
* @param {boolean} [options.includeSearch=true] - Render filter/search input header
|
|
364
|
-
* @param {string} [options.searchPlaceholder='Filter...'] - Search placeholder
|
|
365
|
-
* @param {string} [options.backToDocsHref=''] - Optional back-link to documentation
|
|
366
|
-
* @param {string} [options.backToDocsLabel='Documentation'] - Label for back-link
|
|
367
|
-
* @param {string} [options.itemClassName=''] - Additional CSS class for endpoint link items
|
|
368
|
-
* @param {boolean} [options.includeOverview=true] - Include overview/info link
|
|
369
|
-
* @param {'3-char'|'full'} [options.methodBadgeFormat='3-char'] - Badge format (with `as-colored-block`)
|
|
370
|
-
* @param {'false'|'as-plain-text'|'as-colored-text'|'as-colored-block'} [options.showMethodInNavBar='as-colored-block'] -
|
|
371
|
-
* How each operation shows its HTTP method, as `show-method-in-nav-bar` of <rapi-doc> (also accepted under that name)
|
|
372
|
-
* @param {boolean|string} [options.usePathInNav=false] - Display endpoint path instead of summary (also accepted as
|
|
373
|
-
* `use-path-in-nav-bar`, `'true'` or `true`)
|
|
374
|
-
* @returns {string} Standard RapiDoc navigation HTML
|
|
375
|
-
*/
|
|
376
|
-
export function renderNavBarHtml(resolvedSpec = {}, options = {}) {
|
|
377
|
-
const {
|
|
378
|
-
basePath = "",
|
|
379
|
-
activeHref = "",
|
|
380
|
-
includeSearch = true,
|
|
381
|
-
searchPlaceholder = "Filter...",
|
|
382
|
-
backToDocsHref = "",
|
|
383
|
-
backToDocsLabel = "Documentation",
|
|
384
|
-
itemClassName = "",
|
|
385
|
-
includeOverview = true,
|
|
386
|
-
methodBadgeFormat = "3-char",
|
|
387
|
-
routingMode = options.routingMode ?? options["routing-mode"] ?? "single-page",
|
|
388
|
-
} = options;
|
|
389
|
-
// The same values as <rapi-doc>'s show-method-in-nav-bar and use-path-in-nav-bar; anything else is the default.
|
|
390
|
-
const methodStyle = options.showMethodInNavBar ?? options["show-method-in-nav-bar"];
|
|
391
|
-
const showMethodInNavBar = METHOD_STYLES.includes(String(methodStyle))
|
|
392
|
-
? String(methodStyle)
|
|
393
|
-
: "as-colored-block";
|
|
394
|
-
const pathOption = options.usePathInNav ?? options["use-path-in-nav-bar"];
|
|
395
|
-
const usePathInNav = pathOption === true || pathOption === "true";
|
|
396
|
-
|
|
397
|
-
const slugger = new OperationSlugger();
|
|
398
|
-
const cleanBase = basePath.replace(/\/$/, "");
|
|
399
|
-
|
|
400
|
-
const ctx = {
|
|
401
|
-
basePath,
|
|
402
|
-
cleanBase,
|
|
403
|
-
activeHref,
|
|
404
|
-
routingMode,
|
|
405
|
-
itemClassName,
|
|
406
|
-
includeOverview,
|
|
407
|
-
methodBadgeFormat,
|
|
408
|
-
showMethodInNavBar,
|
|
409
|
-
usePathInNav,
|
|
410
|
-
resolvedSpec,
|
|
411
|
-
slugger,
|
|
412
|
-
};
|
|
413
|
-
|
|
414
|
-
let overviewHtml = "";
|
|
415
|
-
if (includeOverview && resolvedSpec.info) {
|
|
416
|
-
const overviewTitle = resolvedSpec.info.title || "Overview";
|
|
417
|
-
const href = `${basePath}#overview`;
|
|
418
|
-
const isActive = Boolean(
|
|
419
|
-
activeHref &&
|
|
420
|
-
(activeHref === href ||
|
|
421
|
-
activeHref === basePath ||
|
|
422
|
-
activeHref === `${basePath}/` ||
|
|
423
|
-
activeHref.endsWith("#overview")),
|
|
424
|
-
);
|
|
425
|
-
overviewHtml = `
|
|
426
|
-
<a
|
|
427
|
-
href="${escapeHtml(href)}"
|
|
428
|
-
class="nav-bar-info ${isActive ? "active" : ""}"
|
|
429
|
-
id="link-overview"
|
|
430
|
-
data-action="navigate"
|
|
431
|
-
data-content-id="overview"
|
|
432
|
-
tabindex="0"
|
|
433
|
-
part="section-navbar-item section-navbar-overview"
|
|
434
|
-
>
|
|
435
|
-
<span class="nav-bar-info-title">${escapeHtml(overviewTitle)}</span>
|
|
436
|
-
</a>`;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
let bodyHtml;
|
|
440
|
-
if (resolvedSpec.tagGroups && resolvedSpec.tagGroups.length > 0) {
|
|
441
|
-
const assignedTagNames = new Set();
|
|
442
|
-
const groupsHtml = resolvedSpec.tagGroups
|
|
443
|
-
.filter((g) => !isInternalOrHidden(g))
|
|
444
|
-
.map((group) => {
|
|
445
|
-
(group.tags || []).forEach((t) => assignedTagNames.add((t.name || "").toLowerCase()));
|
|
446
|
-
return renderGroup(group, ctx);
|
|
447
|
-
})
|
|
448
|
-
.filter(Boolean)
|
|
449
|
-
.join("");
|
|
450
|
-
|
|
451
|
-
const remainingTagsHtml = (resolvedSpec.tags || [])
|
|
452
|
-
.filter((t) => !assignedTagNames.has((t.name || "").toLowerCase()) && !isInternalOrHidden(t))
|
|
453
|
-
.map((t) => renderTag(t, ctx))
|
|
454
|
-
.filter(Boolean)
|
|
455
|
-
.join("");
|
|
456
|
-
|
|
457
|
-
bodyHtml = groupsHtml + remainingTagsHtml;
|
|
458
|
-
} else {
|
|
459
|
-
bodyHtml = (resolvedSpec.tags || [])
|
|
460
|
-
.filter((t) => !isInternalOrHidden(t))
|
|
461
|
-
.map((t) => renderTag(t, ctx))
|
|
462
|
-
.filter(Boolean)
|
|
463
|
-
.join("");
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
// Dedicated in-place filter input header (without redundant SEARCH modal button)
|
|
467
|
-
const searchHeaderHtml = includeSearch ? getNavFilterHtml({ searchPlaceholder }) : "";
|
|
468
|
-
|
|
469
|
-
const backLinkHtml = backToDocsHref
|
|
470
|
-
? `
|
|
471
|
-
<div class="portal-sidebar-footer-link">
|
|
472
|
-
<a href="${escapeHtml(backToDocsHref)}" class="nav-bar-path">
|
|
473
|
-
<span class="nav-bar-path-label">← ${escapeHtml(backToDocsLabel)}</span>
|
|
474
|
-
</a>
|
|
475
|
-
</div>`
|
|
476
|
-
: "";
|
|
477
|
-
|
|
478
|
-
return `
|
|
479
|
-
${searchHeaderHtml}
|
|
480
|
-
<div class="nav-scroll" tabindex="-1" part="section-navbar-scroll">
|
|
481
|
-
${overviewHtml}
|
|
482
|
-
${bodyHtml}
|
|
483
|
-
${backLinkHtml}
|
|
484
|
-
</div>`;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
export { bindNavBar };
|
|
488
|
-
|
|
489
|
-
export default {
|
|
490
|
-
getSpecNavigation,
|
|
491
|
-
getNavBarHtml,
|
|
492
|
-
renderNavBarHtml,
|
|
493
|
-
getNavFilterHtml,
|
|
494
|
-
getMethodBadge,
|
|
495
|
-
isInternalOrHidden,
|
|
496
|
-
bindNavBar,
|
|
497
|
-
};
|