@aravindc26/velu 0.11.0 → 0.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +15 -6
- package/schema/velu.schema.json +1251 -115
- package/src/build.ts +1121 -304
- package/src/cli.ts +90 -26
- package/src/engine/_server.mjs +1684 -277
- package/src/engine/app/(docs)/[...slug]/layout.tsx +371 -0
- package/src/engine/app/(docs)/[...slug]/page.tsx +926 -0
- package/src/engine/app/api/proxy/route.ts +23 -0
- package/src/engine/app/copy-page.css +59 -1
- package/src/engine/app/global.css +3157 -3
- package/src/engine/app/layout.tsx +56 -1
- package/src/engine/app/llms-file/route.ts +87 -0
- package/src/engine/app/llms-full-file/route.ts +62 -0
- package/src/engine/app/md-file/[...slug]/route.ts +409 -0
- package/src/engine/app/page.tsx +45 -0
- package/src/engine/app/robots.txt/route.ts +63 -0
- package/src/engine/app/rss-file/[...slug]/route.ts +169 -0
- package/src/engine/app/sitemap.xml/route.ts +82 -0
- package/src/engine/components/assistant.tsx +16 -5
- package/src/engine/components/changelog-filters.tsx +114 -0
- package/src/engine/components/code-group.tsx +383 -0
- package/src/engine/components/color.tsx +118 -0
- package/src/engine/components/expandable.tsx +77 -0
- package/src/engine/components/icon.tsx +136 -0
- package/src/engine/components/image-zoom-fallback.tsx +147 -0
- package/src/engine/components/image.tsx +111 -0
- package/src/engine/components/manual-api-playground.tsx +154 -0
- package/src/engine/components/mermaid.tsx +142 -0
- package/src/engine/components/openapi-toc-sync.tsx +59 -0
- package/src/engine/components/openapi.tsx +1682 -0
- package/src/engine/components/page-feedback.tsx +153 -0
- package/src/engine/components/product-switcher.tsx +27 -3
- package/src/engine/components/prompt.tsx +90 -0
- package/src/engine/components/providers.tsx +1 -6
- package/src/engine/components/search.tsx +4 -0
- package/src/engine/components/sidebar-links.tsx +13 -15
- package/src/engine/components/synced-tabs.tsx +57 -0
- package/src/engine/components/toc-examples.tsx +110 -0
- package/src/engine/components/view.tsx +344 -0
- package/src/engine/generated/redirects.ts +3 -0
- package/src/engine/lib/changelog.ts +246 -0
- package/src/engine/lib/layout.shared.ts +30 -2
- package/src/engine/lib/llms.ts +444 -0
- package/src/engine/lib/navigation-normalize.mjs +481 -412
- package/src/engine/lib/navigation-normalize.ts +261 -54
- package/src/engine/lib/redirects.ts +194 -0
- package/src/engine/lib/source.ts +107 -4
- package/src/engine/lib/velu.ts +368 -2
- package/src/engine/mdx-components.tsx +648 -0
- package/src/engine/middleware.ts +66 -0
- package/src/engine/public/icons/cursor-dark.svg +12 -0
- package/src/engine/public/icons/cursor-light.svg +12 -0
- package/src/engine/source.config.ts +98 -1
- package/src/engine/src/components/PageTitle.astro +16 -5
- package/src/engine/src/lib/velu.ts +11 -3
- package/src/navigation-normalize.ts +252 -54
- package/src/themes.ts +6 -6
- package/src/validate.ts +119 -6
- package/src/engine/app/(docs)/[[...slug]]/layout.tsx +0 -87
- package/src/engine/app/(docs)/[[...slug]]/page.tsx +0 -146
|
@@ -6,16 +6,21 @@ interface NavLink {
|
|
|
6
6
|
href: string;
|
|
7
7
|
label: string;
|
|
8
8
|
icon?: string;
|
|
9
|
+
iconType?: string;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
interface NavGroup {
|
|
12
13
|
group: string;
|
|
13
14
|
slug: string;
|
|
14
15
|
icon?: string;
|
|
16
|
+
iconType?: string;
|
|
17
|
+
version?: string;
|
|
15
18
|
tag?: string;
|
|
16
19
|
expanded?: boolean;
|
|
17
20
|
description?: string;
|
|
18
21
|
hidden?: boolean;
|
|
22
|
+
openapi?: OpenApiValue;
|
|
23
|
+
asyncapi?: OpenApiValue;
|
|
19
24
|
pages: NavEntry[];
|
|
20
25
|
}
|
|
21
26
|
|
|
@@ -23,12 +28,17 @@ interface NavTab {
|
|
|
23
28
|
tab: string;
|
|
24
29
|
slug: string;
|
|
25
30
|
icon?: string;
|
|
31
|
+
iconType?: string;
|
|
32
|
+
version?: string;
|
|
26
33
|
href?: string;
|
|
34
|
+
openapi?: OpenApiValue;
|
|
35
|
+
asyncapi?: OpenApiValue;
|
|
27
36
|
pages?: Array<string | NavSeparator | NavLink>;
|
|
28
37
|
groups?: NavGroup[];
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
type NavEntry = string | NavGroup | NavSeparator | NavLink;
|
|
41
|
+
type OpenApiValue = string | string[] | { source: string | string[]; directory?: string };
|
|
32
42
|
|
|
33
43
|
function isObject(value: unknown): value is Record<string, unknown> {
|
|
34
44
|
return typeof value === "object" && value !== null;
|
|
@@ -89,10 +99,45 @@ function uniqueSlug(base: string, used: Set<string>): string {
|
|
|
89
99
|
return candidate;
|
|
90
100
|
}
|
|
91
101
|
|
|
102
|
+
function normalizeOpenApiValue(value: unknown): OpenApiValue | undefined {
|
|
103
|
+
if (typeof value === "string") {
|
|
104
|
+
const trimmed = value.trim();
|
|
105
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
106
|
+
}
|
|
107
|
+
if (Array.isArray(value)) {
|
|
108
|
+
const items = value
|
|
109
|
+
.filter((entry): entry is string => typeof entry === "string")
|
|
110
|
+
.map((entry) => entry.trim())
|
|
111
|
+
.filter((entry) => entry.length > 0);
|
|
112
|
+
return items.length > 0 ? items : undefined;
|
|
113
|
+
}
|
|
114
|
+
if (isObject(value)) {
|
|
115
|
+
const source = normalizeOpenApiValue(value.source);
|
|
116
|
+
if (source === undefined || typeof source === "object" && !Array.isArray(source)) return undefined;
|
|
117
|
+
const rawDirectory = typeof value.directory === "string" ? value.directory.trim() : "";
|
|
118
|
+
const directory = rawDirectory ? rawDirectory.replace(/\\/g, "/").replace(/^\/+/, "").replace(/\/+$/, "") : undefined;
|
|
119
|
+
return directory ? { source, directory } : { source };
|
|
120
|
+
}
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function resolveOpenApiValue(local: unknown, inherited?: OpenApiValue): OpenApiValue | undefined {
|
|
125
|
+
return normalizeOpenApiValue(local) ?? inherited;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function resolveVersionValue(local: unknown, inherited?: string): string | undefined {
|
|
129
|
+
if (typeof local === "string") {
|
|
130
|
+
const trimmed = local.trim();
|
|
131
|
+
if (trimmed.length > 0) return trimmed;
|
|
132
|
+
}
|
|
133
|
+
return inherited;
|
|
134
|
+
}
|
|
135
|
+
|
|
92
136
|
|
|
93
137
|
function normalizeLink(value: NavLink): NavLink {
|
|
94
138
|
const out: NavLink = { href: value.href, label: value.label };
|
|
95
139
|
if (typeof value.icon === "string" && value.icon.length > 0) out.icon = value.icon;
|
|
140
|
+
if (typeof value.iconType === "string" && value.iconType.length > 0) out.iconType = value.iconType;
|
|
96
141
|
return out;
|
|
97
142
|
}
|
|
98
143
|
|
|
@@ -100,7 +145,11 @@ function normalizeAnchorLink(value: Record<string, unknown>): NavLink {
|
|
|
100
145
|
const href = typeof value.href === "string" ? value.href : "#";
|
|
101
146
|
const label = typeof value.anchor === "string" ? value.anchor : "Link";
|
|
102
147
|
const icon = typeof value.icon === "string" ? value.icon : undefined;
|
|
103
|
-
|
|
148
|
+
const iconType = typeof value.iconType === "string" ? value.iconType : undefined;
|
|
149
|
+
const out: NavLink = { href, label };
|
|
150
|
+
if (icon) out.icon = icon;
|
|
151
|
+
if (iconType) out.iconType = iconType;
|
|
152
|
+
return out;
|
|
104
153
|
}
|
|
105
154
|
|
|
106
155
|
function hasContent(value: Record<string, unknown>): boolean {
|
|
@@ -109,7 +158,9 @@ function hasContent(value: Record<string, unknown>): boolean {
|
|
|
109
158
|
(Array.isArray(value.groups) && value.groups.length > 0) ||
|
|
110
159
|
(Array.isArray(value.menu) && value.menu.length > 0) ||
|
|
111
160
|
(Array.isArray(value.tabs) && value.tabs.length > 0) ||
|
|
112
|
-
(Array.isArray(value.dropdowns) && value.dropdowns.length > 0)
|
|
161
|
+
(Array.isArray(value.dropdowns) && value.dropdowns.length > 0) ||
|
|
162
|
+
normalizeOpenApiValue(value.openapi) !== undefined ||
|
|
163
|
+
normalizeOpenApiValue(value.asyncapi) !== undefined;
|
|
113
164
|
|
|
114
165
|
const hasAnchors =
|
|
115
166
|
Array.isArray(value.anchors) &&
|
|
@@ -127,57 +178,105 @@ function hasContent(value: Record<string, unknown>): boolean {
|
|
|
127
178
|
return hasSimple || hasAnchors;
|
|
128
179
|
}
|
|
129
180
|
|
|
130
|
-
function normalizeGroup(
|
|
181
|
+
function normalizeGroup(
|
|
182
|
+
rawGroup: Record<string, unknown>,
|
|
183
|
+
usedGroupSlugs: Set<string>,
|
|
184
|
+
inheritedOpenApi?: OpenApiValue,
|
|
185
|
+
inheritedVersion?: string,
|
|
186
|
+
): NavGroup {
|
|
131
187
|
const groupName = typeof rawGroup.group === "string" ? rawGroup.group : "Group";
|
|
132
188
|
const rawSlug = typeof rawGroup.slug === "string" ? rawGroup.slug : groupName;
|
|
133
189
|
const groupSlug = uniqueSlug(slugify(rawSlug, "group"), usedGroupSlugs);
|
|
190
|
+
const openapi = resolveOpenApiValue(rawGroup.openapi, inheritedOpenApi);
|
|
191
|
+
const asyncapi = normalizeOpenApiValue(rawGroup.asyncapi);
|
|
192
|
+
const version = resolveVersionValue(rawGroup.version, inheritedVersion);
|
|
134
193
|
|
|
135
194
|
const childUsedSlugs = new Set<string>();
|
|
136
|
-
const pages = collectEntries(rawGroup, childUsedSlugs);
|
|
195
|
+
const pages = collectEntries(rawGroup, childUsedSlugs, openapi, version);
|
|
137
196
|
|
|
138
197
|
const out: NavGroup = { group: groupName, slug: groupSlug, pages };
|
|
139
198
|
if (typeof rawGroup.icon === "string") out.icon = rawGroup.icon;
|
|
199
|
+
if (typeof rawGroup.iconType === "string") out.iconType = rawGroup.iconType;
|
|
200
|
+
if (version !== undefined) out.version = version;
|
|
140
201
|
if (typeof rawGroup.tag === "string") out.tag = rawGroup.tag;
|
|
141
202
|
if (typeof rawGroup.expanded === "boolean") out.expanded = rawGroup.expanded;
|
|
142
203
|
if (typeof rawGroup.description === "string") out.description = rawGroup.description;
|
|
143
204
|
if (typeof rawGroup.hidden === "boolean") out.hidden = rawGroup.hidden;
|
|
205
|
+
if (openapi !== undefined) out.openapi = openapi;
|
|
206
|
+
if (asyncapi !== undefined) out.asyncapi = asyncapi;
|
|
144
207
|
return out;
|
|
145
208
|
}
|
|
146
209
|
|
|
147
|
-
function normalizeMenuItem(
|
|
210
|
+
function normalizeMenuItem(
|
|
211
|
+
rawItem: Record<string, unknown>,
|
|
212
|
+
usedGroupSlugs: Set<string>,
|
|
213
|
+
inheritedOpenApi?: OpenApiValue,
|
|
214
|
+
inheritedVersion?: string,
|
|
215
|
+
): NavGroup {
|
|
148
216
|
const name = typeof rawItem.item === "string" ? rawItem.item : "Menu";
|
|
149
217
|
const rawSlug = typeof rawItem.slug === "string" ? rawItem.slug : name;
|
|
150
218
|
const slug = uniqueSlug(slugify(rawSlug, "menu"), usedGroupSlugs);
|
|
219
|
+
const openapi = resolveOpenApiValue(rawItem.openapi, inheritedOpenApi);
|
|
220
|
+
const asyncapi = normalizeOpenApiValue(rawItem.asyncapi);
|
|
221
|
+
const version = resolveVersionValue(rawItem.version, inheritedVersion);
|
|
151
222
|
|
|
152
223
|
const nestedGroupSlugs = new Set<string>();
|
|
153
|
-
const pages = collectEntries(rawItem, nestedGroupSlugs);
|
|
224
|
+
const pages = collectEntries(rawItem, nestedGroupSlugs, openapi, version);
|
|
154
225
|
const out: NavGroup = { group: name, slug, pages };
|
|
155
226
|
if (typeof rawItem.icon === "string") out.icon = rawItem.icon;
|
|
227
|
+
if (typeof rawItem.iconType === "string") out.iconType = rawItem.iconType;
|
|
228
|
+
if (version !== undefined) out.version = version;
|
|
229
|
+
if (openapi !== undefined) out.openapi = openapi;
|
|
230
|
+
if (asyncapi !== undefined) out.asyncapi = asyncapi;
|
|
156
231
|
return out;
|
|
157
232
|
}
|
|
158
233
|
|
|
159
|
-
function normalizeTabAsGroup(
|
|
234
|
+
function normalizeTabAsGroup(
|
|
235
|
+
rawTab: Record<string, unknown>,
|
|
236
|
+
usedGroupSlugs: Set<string>,
|
|
237
|
+
inheritedOpenApi?: OpenApiValue,
|
|
238
|
+
inheritedVersion?: string,
|
|
239
|
+
): NavGroup {
|
|
160
240
|
const tabName = typeof rawTab.tab === "string" ? rawTab.tab : "Tab";
|
|
161
241
|
const rawSlug = typeof rawTab.slug === "string" ? rawTab.slug : tabName;
|
|
162
242
|
const slug = uniqueSlug(slugify(rawSlug, "tab"), usedGroupSlugs);
|
|
243
|
+
const openapi = resolveOpenApiValue(rawTab.openapi, inheritedOpenApi);
|
|
244
|
+
const asyncapi = normalizeOpenApiValue(rawTab.asyncapi);
|
|
245
|
+
const version = resolveVersionValue(rawTab.version, inheritedVersion);
|
|
163
246
|
const nestedGroupSlugs = new Set<string>();
|
|
164
|
-
const pages = collectEntries(rawTab, nestedGroupSlugs);
|
|
247
|
+
const pages = collectEntries(rawTab, nestedGroupSlugs, openapi, version);
|
|
165
248
|
|
|
166
249
|
if (typeof rawTab.href === "string" && rawTab.href.length > 0 && !hasContent(rawTab)) {
|
|
167
|
-
pages.push({
|
|
250
|
+
pages.push({
|
|
251
|
+
href: rawTab.href,
|
|
252
|
+
label: tabName,
|
|
253
|
+
...(typeof rawTab.icon === "string" ? { icon: rawTab.icon } : {}),
|
|
254
|
+
...(typeof rawTab.iconType === "string" ? { iconType: rawTab.iconType } : {}),
|
|
255
|
+
});
|
|
168
256
|
}
|
|
169
257
|
|
|
170
258
|
const out: NavGroup = { group: tabName, slug, pages };
|
|
171
259
|
if (typeof rawTab.icon === "string") out.icon = rawTab.icon;
|
|
260
|
+
if (typeof rawTab.iconType === "string") out.iconType = rawTab.iconType;
|
|
261
|
+
if (version !== undefined) out.version = version;
|
|
262
|
+
if (openapi !== undefined) out.openapi = openapi;
|
|
263
|
+
if (asyncapi !== undefined) out.asyncapi = asyncapi;
|
|
172
264
|
return out;
|
|
173
265
|
}
|
|
174
266
|
|
|
175
|
-
function normalizeDropdownAsGroup(
|
|
267
|
+
function normalizeDropdownAsGroup(
|
|
268
|
+
rawDropdown: Record<string, unknown>,
|
|
269
|
+
usedGroupSlugs: Set<string>,
|
|
270
|
+
inheritedOpenApi?: OpenApiValue,
|
|
271
|
+
inheritedVersion?: string,
|
|
272
|
+
): NavGroup {
|
|
176
273
|
return normalizeTabAsGroup(
|
|
177
274
|
{
|
|
178
275
|
tab: rawDropdown.dropdown,
|
|
179
276
|
slug: rawDropdown.slug,
|
|
180
277
|
icon: rawDropdown.icon,
|
|
278
|
+
iconType: rawDropdown.iconType,
|
|
279
|
+
version: rawDropdown.version,
|
|
181
280
|
href: rawDropdown.href,
|
|
182
281
|
groups: rawDropdown.groups,
|
|
183
282
|
pages: rawDropdown.pages,
|
|
@@ -185,39 +284,69 @@ function normalizeDropdownAsGroup(rawDropdown: Record<string, unknown>, usedGrou
|
|
|
185
284
|
anchors: rawDropdown.anchors,
|
|
186
285
|
dropdowns: rawDropdown.dropdowns,
|
|
187
286
|
tabs: rawDropdown.tabs,
|
|
287
|
+
openapi: rawDropdown.openapi,
|
|
288
|
+
asyncapi: rawDropdown.asyncapi,
|
|
188
289
|
},
|
|
189
|
-
usedGroupSlugs
|
|
290
|
+
usedGroupSlugs,
|
|
291
|
+
inheritedOpenApi,
|
|
292
|
+
inheritedVersion
|
|
190
293
|
);
|
|
191
294
|
}
|
|
192
295
|
|
|
193
|
-
function normalizeAnchorAsGroup(
|
|
296
|
+
function normalizeAnchorAsGroup(
|
|
297
|
+
rawAnchor: Record<string, unknown>,
|
|
298
|
+
usedGroupSlugs: Set<string>,
|
|
299
|
+
inheritedOpenApi?: OpenApiValue,
|
|
300
|
+
inheritedVersion?: string,
|
|
301
|
+
): NavGroup {
|
|
194
302
|
const anchorName = typeof rawAnchor.anchor === "string" ? rawAnchor.anchor : "Anchor";
|
|
195
303
|
const rawSlug = typeof rawAnchor.slug === "string" ? rawAnchor.slug : anchorName;
|
|
196
304
|
const slug = uniqueSlug(slugify(rawSlug, "anchor"), usedGroupSlugs);
|
|
305
|
+
const openapi = resolveOpenApiValue(rawAnchor.openapi, inheritedOpenApi);
|
|
306
|
+
const asyncapi = normalizeOpenApiValue(rawAnchor.asyncapi);
|
|
307
|
+
const version = resolveVersionValue(rawAnchor.version, inheritedVersion);
|
|
197
308
|
const nestedGroupSlugs = new Set<string>();
|
|
198
|
-
const pages = collectEntries(rawAnchor, nestedGroupSlugs);
|
|
309
|
+
const pages = collectEntries(rawAnchor, nestedGroupSlugs, openapi, version);
|
|
199
310
|
|
|
200
311
|
const out: NavGroup = { group: anchorName, slug, pages };
|
|
201
312
|
if (typeof rawAnchor.icon === "string") out.icon = rawAnchor.icon;
|
|
313
|
+
if (typeof rawAnchor.iconType === "string") out.iconType = rawAnchor.iconType;
|
|
314
|
+
if (version !== undefined) out.version = version;
|
|
315
|
+
if (openapi !== undefined) out.openapi = openapi;
|
|
316
|
+
if (asyncapi !== undefined) out.asyncapi = asyncapi;
|
|
202
317
|
return out;
|
|
203
318
|
}
|
|
204
319
|
|
|
205
|
-
function collectEntries(
|
|
320
|
+
function collectEntries(
|
|
321
|
+
rawSection: Record<string, unknown>,
|
|
322
|
+
usedGroupSlugs: Set<string>,
|
|
323
|
+
inheritedOpenApi?: OpenApiValue,
|
|
324
|
+
inheritedVersion?: string,
|
|
325
|
+
): NavEntry[] {
|
|
206
326
|
const entries: NavEntry[] = [];
|
|
327
|
+
const hasNonMenuContent =
|
|
328
|
+
(Array.isArray(rawSection.groups) && rawSection.groups.length > 0) ||
|
|
329
|
+
(Array.isArray(rawSection.pages) && rawSection.pages.length > 0) ||
|
|
330
|
+
(Array.isArray(rawSection.anchors) && rawSection.anchors.length > 0) ||
|
|
331
|
+
(Array.isArray(rawSection.dropdowns) && rawSection.dropdowns.length > 0) ||
|
|
332
|
+
(Array.isArray(rawSection.tabs) && rawSection.tabs.length > 0);
|
|
207
333
|
|
|
208
334
|
for (const item of Array.isArray(rawSection.menu) ? rawSection.menu : []) {
|
|
209
|
-
|
|
335
|
+
// Menu is primarily for topbar dropdown navigation.
|
|
336
|
+
// Only materialize it into page-tree groups when this section has no normal content.
|
|
337
|
+
if (hasNonMenuContent) break;
|
|
338
|
+
if (isMenuItem(item)) entries.push(normalizeMenuItem(item, usedGroupSlugs, inheritedOpenApi, inheritedVersion));
|
|
210
339
|
}
|
|
211
340
|
|
|
212
341
|
for (const group of Array.isArray(rawSection.groups) ? rawSection.groups : []) {
|
|
213
|
-
if (isGroupLike(group)) entries.push(normalizeGroup(group, usedGroupSlugs));
|
|
342
|
+
if (isGroupLike(group)) entries.push(normalizeGroup(group, usedGroupSlugs, inheritedOpenApi, inheritedVersion));
|
|
214
343
|
}
|
|
215
344
|
|
|
216
345
|
for (const item of Array.isArray(rawSection.pages) ? rawSection.pages : []) {
|
|
217
346
|
if (typeof item === "string") entries.push(item);
|
|
218
347
|
else if (isSeparator(item)) entries.push({ separator: item.separator });
|
|
219
348
|
else if (isLink(item)) entries.push(normalizeLink(item));
|
|
220
|
-
else if (isGroupLike(item)) entries.push(normalizeGroup(item, usedGroupSlugs));
|
|
349
|
+
else if (isGroupLike(item)) entries.push(normalizeGroup(item, usedGroupSlugs, inheritedOpenApi, inheritedVersion));
|
|
221
350
|
}
|
|
222
351
|
|
|
223
352
|
for (const anchor of Array.isArray(rawSection.anchors) ? rawSection.anchors : []) {
|
|
@@ -225,29 +354,42 @@ function collectEntries(rawSection: Record<string, unknown>, usedGroupSlugs: Set
|
|
|
225
354
|
|
|
226
355
|
const hrefOnly = typeof anchor.href === "string" && anchor.href.length > 0 && !hasContent(anchor);
|
|
227
356
|
if (hrefOnly) entries.push(normalizeAnchorLink(anchor));
|
|
228
|
-
else entries.push(normalizeAnchorAsGroup(anchor, usedGroupSlugs));
|
|
357
|
+
else entries.push(normalizeAnchorAsGroup(anchor, usedGroupSlugs, inheritedOpenApi, inheritedVersion));
|
|
229
358
|
}
|
|
230
359
|
|
|
231
360
|
for (const dropdown of Array.isArray(rawSection.dropdowns) ? rawSection.dropdowns : []) {
|
|
232
|
-
if (isDropdownLike(dropdown)) entries.push(normalizeDropdownAsGroup(dropdown, usedGroupSlugs));
|
|
361
|
+
if (isDropdownLike(dropdown)) entries.push(normalizeDropdownAsGroup(dropdown, usedGroupSlugs, inheritedOpenApi, inheritedVersion));
|
|
233
362
|
}
|
|
234
363
|
|
|
235
364
|
for (const tab of Array.isArray(rawSection.tabs) ? rawSection.tabs : []) {
|
|
236
|
-
if (isTabLike(tab)) entries.push(normalizeTabAsGroup(tab, usedGroupSlugs));
|
|
365
|
+
if (isTabLike(tab)) entries.push(normalizeTabAsGroup(tab, usedGroupSlugs, inheritedOpenApi, inheritedVersion));
|
|
237
366
|
}
|
|
238
367
|
|
|
239
368
|
return entries;
|
|
240
369
|
}
|
|
241
370
|
|
|
242
|
-
function normalizeTab(
|
|
371
|
+
function normalizeTab(
|
|
372
|
+
rawTab: Record<string, unknown>,
|
|
373
|
+
usedTabSlugs: Set<string>,
|
|
374
|
+
slugPrefix: string,
|
|
375
|
+
inheritedOpenApi?: OpenApiValue,
|
|
376
|
+
inheritedVersion?: string,
|
|
377
|
+
): NavTab {
|
|
243
378
|
const tabName = typeof rawTab.tab === "string" ? rawTab.tab : "Tab";
|
|
244
379
|
const rawSlug = typeof rawTab.slug === "string" ? rawTab.slug : tabName;
|
|
245
380
|
const tabSlugPart = slugify(rawSlug, "tab");
|
|
246
381
|
const fullSlug = slugPrefix ? `${slugPrefix}/${tabSlugPart}` : tabSlugPart;
|
|
247
382
|
const slug = uniqueSlug(fullSlug, usedTabSlugs);
|
|
383
|
+
const openapi = resolveOpenApiValue(rawTab.openapi, inheritedOpenApi);
|
|
384
|
+
const asyncapi = normalizeOpenApiValue(rawTab.asyncapi);
|
|
385
|
+
const version = resolveVersionValue(rawTab.version, inheritedVersion);
|
|
248
386
|
|
|
249
387
|
const out: NavTab = { tab: tabName, slug };
|
|
250
388
|
if (typeof rawTab.icon === "string") out.icon = rawTab.icon;
|
|
389
|
+
if (typeof rawTab.iconType === "string") out.iconType = rawTab.iconType;
|
|
390
|
+
if (version !== undefined) out.version = version;
|
|
391
|
+
if (openapi !== undefined) out.openapi = openapi;
|
|
392
|
+
if (asyncapi !== undefined) out.asyncapi = asyncapi;
|
|
251
393
|
|
|
252
394
|
if (typeof rawTab.href === "string" && rawTab.href.length > 0 && !hasContent(rawTab)) {
|
|
253
395
|
out.href = rawTab.href;
|
|
@@ -255,7 +397,7 @@ function normalizeTab(rawTab: Record<string, unknown>, usedTabSlugs: Set<string>
|
|
|
255
397
|
}
|
|
256
398
|
|
|
257
399
|
const groupSlugSet = new Set<string>();
|
|
258
|
-
const entries = collectEntries(rawTab, groupSlugSet);
|
|
400
|
+
const entries = collectEntries(rawTab, groupSlugSet, openapi, version);
|
|
259
401
|
const groups: NavGroup[] = [];
|
|
260
402
|
const pages: Array<string | NavSeparator | NavLink> = [];
|
|
261
403
|
|
|
@@ -269,57 +411,92 @@ function normalizeTab(rawTab: Record<string, unknown>, usedTabSlugs: Set<string>
|
|
|
269
411
|
return out;
|
|
270
412
|
}
|
|
271
413
|
|
|
272
|
-
function normalizeDropdownToTab(
|
|
414
|
+
function normalizeDropdownToTab(
|
|
415
|
+
rawDropdown: Record<string, unknown>,
|
|
416
|
+
usedTabSlugs: Set<string>,
|
|
417
|
+
slugPrefix: string,
|
|
418
|
+
inheritedOpenApi?: OpenApiValue,
|
|
419
|
+
inheritedVersion?: string,
|
|
420
|
+
): NavTab {
|
|
273
421
|
return normalizeTab(
|
|
274
422
|
{
|
|
275
423
|
tab: rawDropdown.dropdown,
|
|
276
424
|
slug: rawDropdown.slug,
|
|
277
425
|
icon: rawDropdown.icon,
|
|
426
|
+
iconType: rawDropdown.iconType,
|
|
427
|
+
version: rawDropdown.version,
|
|
278
428
|
href: rawDropdown.href,
|
|
279
429
|
groups: rawDropdown.groups,
|
|
280
430
|
pages: rawDropdown.pages,
|
|
281
431
|
menu: rawDropdown.menu,
|
|
282
432
|
anchors: rawDropdown.anchors,
|
|
283
433
|
dropdowns: rawDropdown.dropdowns,
|
|
284
|
-
|
|
285
|
-
|
|
434
|
+
tabs: rawDropdown.tabs,
|
|
435
|
+
openapi: rawDropdown.openapi,
|
|
436
|
+
asyncapi: rawDropdown.asyncapi,
|
|
437
|
+
},
|
|
286
438
|
usedTabSlugs,
|
|
287
|
-
slugPrefix
|
|
439
|
+
slugPrefix,
|
|
440
|
+
inheritedOpenApi,
|
|
441
|
+
inheritedVersion
|
|
288
442
|
);
|
|
289
443
|
}
|
|
290
444
|
|
|
291
|
-
function normalizeTabList(
|
|
445
|
+
function normalizeTabList(
|
|
446
|
+
rawTabs: unknown[],
|
|
447
|
+
usedTabSlugs: Set<string>,
|
|
448
|
+
slugPrefix = "",
|
|
449
|
+
inheritedOpenApi?: OpenApiValue,
|
|
450
|
+
inheritedVersion?: string,
|
|
451
|
+
): NavTab[] {
|
|
292
452
|
const tabs: NavTab[] = [];
|
|
293
453
|
for (const item of rawTabs) {
|
|
294
|
-
if (isTabLike(item)) tabs.push(normalizeTab(item, usedTabSlugs, slugPrefix));
|
|
454
|
+
if (isTabLike(item)) tabs.push(normalizeTab(item, usedTabSlugs, slugPrefix, inheritedOpenApi, inheritedVersion));
|
|
295
455
|
}
|
|
296
456
|
return tabs;
|
|
297
457
|
}
|
|
298
458
|
|
|
299
|
-
function normalizeDropdownList(
|
|
459
|
+
function normalizeDropdownList(
|
|
460
|
+
rawDropdowns: unknown[],
|
|
461
|
+
usedTabSlugs: Set<string>,
|
|
462
|
+
slugPrefix = "",
|
|
463
|
+
inheritedOpenApi?: OpenApiValue,
|
|
464
|
+
inheritedVersion?: string,
|
|
465
|
+
): NavTab[] {
|
|
300
466
|
const tabs: NavTab[] = [];
|
|
301
467
|
for (const item of rawDropdowns) {
|
|
302
|
-
if (isDropdownLike(item)) tabs.push(normalizeDropdownToTab(item, usedTabSlugs, slugPrefix));
|
|
468
|
+
if (isDropdownLike(item)) tabs.push(normalizeDropdownToTab(item, usedTabSlugs, slugPrefix, inheritedOpenApi, inheritedVersion));
|
|
303
469
|
}
|
|
304
|
-
return tabs;
|
|
470
|
+
if (inheritedOpenApi === undefined && inheritedVersion === undefined) return tabs;
|
|
471
|
+
return tabs.map((tab) => ({
|
|
472
|
+
...tab,
|
|
473
|
+
openapi: tab.openapi ?? inheritedOpenApi,
|
|
474
|
+
version: tab.version ?? inheritedVersion,
|
|
475
|
+
}));
|
|
305
476
|
}
|
|
306
477
|
|
|
307
|
-
function normalizeNavigationTabs(
|
|
478
|
+
function normalizeNavigationTabs(
|
|
479
|
+
navigation: unknown,
|
|
480
|
+
usedTabSlugs = new Set<string>(),
|
|
481
|
+
inheritedOpenApi?: OpenApiValue,
|
|
482
|
+
): NavTab[] {
|
|
308
483
|
if (!isObject(navigation)) return [];
|
|
309
484
|
|
|
485
|
+
const sectionOpenApi = resolveOpenApiValue(navigation.openapi, inheritedOpenApi);
|
|
310
486
|
const tabs: NavTab[] = [];
|
|
311
487
|
|
|
312
|
-
tabs.push(...normalizeTabList(Array.isArray(navigation.tabs) ? navigation.tabs : [], usedTabSlugs));
|
|
313
|
-
tabs.push(...normalizeDropdownList(Array.isArray(navigation.dropdowns) ? navigation.dropdowns : [], usedTabSlugs));
|
|
488
|
+
tabs.push(...normalizeTabList(Array.isArray(navigation.tabs) ? navigation.tabs : [], usedTabSlugs, "", sectionOpenApi));
|
|
489
|
+
tabs.push(...normalizeDropdownList(Array.isArray(navigation.dropdowns) ? navigation.dropdowns : [], usedTabSlugs, "", sectionOpenApi));
|
|
314
490
|
|
|
315
491
|
if (Array.isArray(navigation.products)) {
|
|
316
492
|
navigation.products.forEach((product, index) => {
|
|
317
493
|
if (!isObject(product)) return;
|
|
318
494
|
const productName = typeof product.product === "string" ? product.product : `Product ${index + 1}`;
|
|
319
495
|
const prefix = slugify(productName, `product-${index + 1}`);
|
|
496
|
+
const productOpenApi = resolveOpenApiValue(product.openapi, sectionOpenApi);
|
|
320
497
|
|
|
321
|
-
tabs.push(...normalizeTabList(Array.isArray(product.tabs) ? product.tabs : [], usedTabSlugs, prefix));
|
|
322
|
-
tabs.push(...normalizeDropdownList(Array.isArray(product.dropdowns) ? product.dropdowns : [], usedTabSlugs, prefix));
|
|
498
|
+
tabs.push(...normalizeTabList(Array.isArray(product.tabs) ? product.tabs : [], usedTabSlugs, prefix, productOpenApi));
|
|
499
|
+
tabs.push(...normalizeDropdownList(Array.isArray(product.dropdowns) ? product.dropdowns : [], usedTabSlugs, prefix, productOpenApi));
|
|
323
500
|
|
|
324
501
|
if (!Array.isArray(product.tabs) && !Array.isArray(product.dropdowns)) {
|
|
325
502
|
if (hasContent(product)) {
|
|
@@ -329,15 +506,19 @@ function normalizeNavigationTabs(navigation: unknown, usedTabSlugs = new Set<str
|
|
|
329
506
|
tab: productName,
|
|
330
507
|
slug: prefix,
|
|
331
508
|
icon: product.icon,
|
|
509
|
+
iconType: product.iconType,
|
|
332
510
|
groups: product.groups,
|
|
333
511
|
pages: product.pages,
|
|
334
512
|
menu: product.menu,
|
|
335
513
|
anchors: product.anchors,
|
|
336
514
|
dropdowns: product.dropdowns,
|
|
337
515
|
tabs: product.tabs,
|
|
338
|
-
|
|
516
|
+
openapi: productOpenApi,
|
|
517
|
+
asyncapi: normalizeOpenApiValue(product.asyncapi),
|
|
518
|
+
},
|
|
339
519
|
usedTabSlugs,
|
|
340
|
-
""
|
|
520
|
+
"",
|
|
521
|
+
productOpenApi
|
|
341
522
|
)
|
|
342
523
|
);
|
|
343
524
|
} else if (typeof product.href === "string" && product.href.length > 0) {
|
|
@@ -347,10 +528,14 @@ function normalizeNavigationTabs(navigation: unknown, usedTabSlugs = new Set<str
|
|
|
347
528
|
tab: productName,
|
|
348
529
|
slug: prefix,
|
|
349
530
|
icon: product.icon,
|
|
531
|
+
iconType: product.iconType,
|
|
350
532
|
href: product.href,
|
|
351
|
-
|
|
533
|
+
openapi: productOpenApi,
|
|
534
|
+
asyncapi: normalizeOpenApiValue(product.asyncapi),
|
|
535
|
+
},
|
|
352
536
|
usedTabSlugs,
|
|
353
|
-
""
|
|
537
|
+
"",
|
|
538
|
+
productOpenApi
|
|
354
539
|
)
|
|
355
540
|
);
|
|
356
541
|
}
|
|
@@ -363,9 +548,10 @@ function normalizeNavigationTabs(navigation: unknown, usedTabSlugs = new Set<str
|
|
|
363
548
|
if (!isObject(version)) return;
|
|
364
549
|
const versionName = typeof version.version === "string" ? version.version : `Version ${index + 1}`;
|
|
365
550
|
const prefix = slugify(versionName, `version-${index + 1}`);
|
|
551
|
+
const versionOpenApi = resolveOpenApiValue(version.openapi, sectionOpenApi);
|
|
366
552
|
|
|
367
|
-
tabs.push(...normalizeTabList(Array.isArray(version.tabs) ? version.tabs : [], usedTabSlugs, prefix));
|
|
368
|
-
tabs.push(...normalizeDropdownList(Array.isArray(version.dropdowns) ? version.dropdowns : [], usedTabSlugs, prefix));
|
|
553
|
+
tabs.push(...normalizeTabList(Array.isArray(version.tabs) ? version.tabs : [], usedTabSlugs, prefix, versionOpenApi));
|
|
554
|
+
tabs.push(...normalizeDropdownList(Array.isArray(version.dropdowns) ? version.dropdowns : [], usedTabSlugs, prefix, versionOpenApi));
|
|
369
555
|
|
|
370
556
|
if (!Array.isArray(version.tabs) && !Array.isArray(version.dropdowns)) {
|
|
371
557
|
if (hasContent(version)) {
|
|
@@ -380,9 +566,12 @@ function normalizeNavigationTabs(navigation: unknown, usedTabSlugs = new Set<str
|
|
|
380
566
|
anchors: version.anchors,
|
|
381
567
|
dropdowns: version.dropdowns,
|
|
382
568
|
tabs: version.tabs,
|
|
383
|
-
|
|
569
|
+
openapi: versionOpenApi,
|
|
570
|
+
asyncapi: normalizeOpenApiValue(version.asyncapi),
|
|
571
|
+
},
|
|
384
572
|
usedTabSlugs,
|
|
385
|
-
""
|
|
573
|
+
"",
|
|
574
|
+
versionOpenApi
|
|
386
575
|
)
|
|
387
576
|
);
|
|
388
577
|
} else if (typeof version.href === "string" && version.href.length > 0) {
|
|
@@ -392,9 +581,12 @@ function normalizeNavigationTabs(navigation: unknown, usedTabSlugs = new Set<str
|
|
|
392
581
|
tab: versionName,
|
|
393
582
|
slug: prefix,
|
|
394
583
|
href: version.href,
|
|
395
|
-
|
|
584
|
+
openapi: versionOpenApi,
|
|
585
|
+
asyncapi: normalizeOpenApiValue(version.asyncapi),
|
|
586
|
+
},
|
|
396
587
|
usedTabSlugs,
|
|
397
|
-
""
|
|
588
|
+
"",
|
|
589
|
+
versionOpenApi
|
|
398
590
|
)
|
|
399
591
|
);
|
|
400
592
|
}
|
|
@@ -408,9 +600,11 @@ function normalizeNavigationTabs(navigation: unknown, usedTabSlugs = new Set<str
|
|
|
408
600
|
|
|
409
601
|
const anchorName = typeof anchor.anchor === "string" ? anchor.anchor : `Anchor ${index + 1}`;
|
|
410
602
|
const prefix = slugify(anchorName, `anchor-${index + 1}`);
|
|
603
|
+
const anchorOpenApi = resolveOpenApiValue(anchor.openapi, sectionOpenApi);
|
|
604
|
+
const anchorVersion = resolveVersionValue(anchor.version);
|
|
411
605
|
|
|
412
606
|
if (Array.isArray(anchor.tabs)) {
|
|
413
|
-
tabs.push(...normalizeTabList(anchor.tabs, usedTabSlugs, prefix));
|
|
607
|
+
tabs.push(...normalizeTabList(anchor.tabs, usedTabSlugs, prefix, anchorOpenApi, anchorVersion));
|
|
414
608
|
} else if (hasContent(anchor)) {
|
|
415
609
|
tabs.push(
|
|
416
610
|
normalizeTab(
|
|
@@ -418,15 +612,21 @@ function normalizeNavigationTabs(navigation: unknown, usedTabSlugs = new Set<str
|
|
|
418
612
|
tab: anchorName,
|
|
419
613
|
slug: prefix,
|
|
420
614
|
icon: anchor.icon,
|
|
615
|
+
iconType: anchor.iconType,
|
|
616
|
+
version: anchorVersion,
|
|
421
617
|
groups: anchor.groups,
|
|
422
618
|
pages: anchor.pages,
|
|
423
619
|
menu: anchor.menu,
|
|
424
620
|
anchors: anchor.anchors,
|
|
425
621
|
dropdowns: anchor.dropdowns,
|
|
426
622
|
tabs: anchor.tabs,
|
|
623
|
+
openapi: anchorOpenApi,
|
|
624
|
+
asyncapi: normalizeOpenApiValue(anchor.asyncapi),
|
|
427
625
|
},
|
|
428
626
|
usedTabSlugs,
|
|
429
|
-
""
|
|
627
|
+
"",
|
|
628
|
+
anchorOpenApi,
|
|
629
|
+
anchorVersion
|
|
430
630
|
)
|
|
431
631
|
);
|
|
432
632
|
}
|
|
@@ -449,38 +649,45 @@ function normalizeNavigationTabs(navigation: unknown, usedTabSlugs = new Set<str
|
|
|
449
649
|
anchors: navigation.anchors,
|
|
450
650
|
dropdowns: navigation.dropdowns,
|
|
451
651
|
tabs: navigation.tabs,
|
|
652
|
+
openapi: sectionOpenApi,
|
|
653
|
+
asyncapi: normalizeOpenApiValue(navigation.asyncapi),
|
|
452
654
|
},
|
|
453
655
|
usedTabSlugs,
|
|
454
|
-
""
|
|
656
|
+
"",
|
|
657
|
+
sectionOpenApi
|
|
455
658
|
)
|
|
456
659
|
);
|
|
457
660
|
}
|
|
458
661
|
|
|
459
662
|
return tabs;
|
|
460
663
|
}
|
|
461
|
-
|
|
462
|
-
function normalizeLanguageEntries(languages: unknown): Array<Record<string, unknown>> {
|
|
664
|
+
function normalizeLanguageEntries(languages: unknown, inheritedOpenApi?: OpenApiValue): Array<Record<string, unknown>> {
|
|
463
665
|
if (!Array.isArray(languages)) return [];
|
|
464
666
|
|
|
465
667
|
return languages
|
|
466
668
|
.filter(isObject)
|
|
467
669
|
.map((entry) => {
|
|
468
670
|
const usedTabSlugs = new Set<string>();
|
|
671
|
+
const languageOpenApi = resolveOpenApiValue(entry.openapi, inheritedOpenApi);
|
|
469
672
|
return {
|
|
470
673
|
...entry,
|
|
471
|
-
tabs: normalizeNavigationTabs(entry, usedTabSlugs),
|
|
674
|
+
tabs: normalizeNavigationTabs(entry, usedTabSlugs, languageOpenApi),
|
|
472
675
|
};
|
|
473
676
|
});
|
|
474
677
|
}
|
|
475
678
|
|
|
476
679
|
export function normalizeConfigNavigation<T extends { navigation?: unknown }>(config: T): T {
|
|
477
680
|
const nav = isObject(config.navigation) ? config.navigation : {};
|
|
681
|
+
const navOpenApi = resolveOpenApiValue((nav as Record<string, unknown>).openapi);
|
|
682
|
+
const navAsyncApi = normalizeOpenApiValue((nav as Record<string, unknown>).asyncapi);
|
|
478
683
|
return {
|
|
479
684
|
...config,
|
|
480
685
|
navigation: {
|
|
481
686
|
...nav,
|
|
482
|
-
|
|
483
|
-
|
|
687
|
+
...(navOpenApi !== undefined ? { openapi: navOpenApi } : {}),
|
|
688
|
+
...(navAsyncApi !== undefined ? { asyncapi: navAsyncApi } : {}),
|
|
689
|
+
tabs: normalizeNavigationTabs(nav, new Set<string>(), navOpenApi),
|
|
690
|
+
languages: normalizeLanguageEntries(nav.languages, navOpenApi),
|
|
484
691
|
products: Array.isArray(nav.products) ? nav.products : [],
|
|
485
692
|
versions: Array.isArray(nav.versions) ? nav.versions : [],
|
|
486
693
|
},
|