sourcey 3.4.10 → 3.5.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 +13 -12
- package/dist/cli.js +46 -4
- package/dist/components/changelog/ChangelogPage.d.ts +5 -0
- package/dist/components/changelog/ChangelogPage.d.ts.map +1 -0
- package/dist/components/changelog/ChangelogPage.js +8 -0
- package/dist/components/changelog/EntryList.d.ts +5 -0
- package/dist/components/changelog/EntryList.d.ts.map +1 -0
- package/dist/components/changelog/EntryList.js +6 -0
- package/dist/components/changelog/TypeBadge.d.ts +6 -0
- package/dist/components/changelog/TypeBadge.d.ts.map +1 -0
- package/dist/components/changelog/TypeBadge.js +14 -0
- package/dist/components/changelog/VersionCard.d.ts +6 -0
- package/dist/components/changelog/VersionCard.d.ts.map +1 -0
- package/dist/components/changelog/VersionCard.js +25 -0
- package/dist/components/layout/Head.d.ts.map +1 -1
- package/dist/components/layout/Head.js +15 -3
- package/dist/components/layout/Page.d.ts.map +1 -1
- package/dist/components/layout/Page.js +12 -6
- package/dist/components/openapi/Introduction.d.ts.map +1 -1
- package/dist/components/openapi/Introduction.js +1 -1
- package/dist/components/openapi/MediaTypeDetails.d.ts +11 -0
- package/dist/components/openapi/MediaTypeDetails.d.ts.map +1 -0
- package/dist/components/openapi/MediaTypeDetails.js +54 -0
- package/dist/components/openapi/Parameters.d.ts.map +1 -1
- package/dist/components/openapi/Parameters.js +6 -1
- package/dist/components/openapi/RequestBody.d.ts.map +1 -1
- package/dist/components/openapi/RequestBody.js +5 -1
- package/dist/components/openapi/Responses.d.ts.map +1 -1
- package/dist/components/openapi/Responses.js +1 -1
- package/dist/components/openapi/Security.d.ts.map +1 -1
- package/dist/components/openapi/Security.js +3 -2
- package/dist/components/openapi/Tags.d.ts.map +1 -1
- package/dist/components/openapi/Tags.js +8 -1
- package/dist/config.d.ts +50 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +50 -4
- package/dist/core/changelog-normalizer.d.ts +8 -0
- package/dist/core/changelog-normalizer.d.ts.map +1 -0
- package/dist/core/changelog-normalizer.js +531 -0
- package/dist/core/doxygen-loader.d.ts.map +1 -1
- package/dist/core/doxygen-loader.js +2 -0
- package/dist/core/loader.js +2 -0
- package/dist/core/markdown-loader.d.ts +21 -0
- package/dist/core/markdown-loader.d.ts.map +1 -1
- package/dist/core/markdown-loader.js +53 -28
- package/dist/core/mcp-normalizer.d.ts.map +1 -1
- package/dist/core/navigation.d.ts +2 -2
- package/dist/core/navigation.d.ts.map +1 -1
- package/dist/core/navigation.js +5 -1
- package/dist/core/normalizer.d.ts.map +1 -1
- package/dist/core/normalizer.js +102 -4
- package/dist/core/parser.d.ts +2 -2
- package/dist/core/parser.d.ts.map +1 -1
- package/dist/core/parser.js +23 -16
- package/dist/core/search-indexer.d.ts +3 -2
- package/dist/core/search-indexer.d.ts.map +1 -1
- package/dist/core/search-indexer.js +50 -17
- package/dist/core/tag-utils.d.ts +5 -0
- package/dist/core/tag-utils.d.ts.map +1 -0
- package/dist/core/tag-utils.js +22 -0
- package/dist/core/types.d.ts +82 -4
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +1 -1
- package/dist/dev-server.d.ts +2 -0
- package/dist/dev-server.d.ts.map +1 -1
- package/dist/dev-server.js +81 -210
- package/dist/index.d.ts +6 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +115 -247
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +7 -3
- package/dist/og/fonts/Inter-Bold.ttf +0 -0
- package/dist/og/fonts/Inter-Regular.ttf +0 -0
- package/dist/og/generate-og-image.d.ts +10 -0
- package/dist/og/generate-og-image.d.ts.map +1 -0
- package/dist/og/generate-og-image.js +198 -0
- package/dist/renderer/changelog-feed.d.ts +18 -0
- package/dist/renderer/changelog-feed.d.ts.map +1 -0
- package/dist/renderer/changelog-feed.js +91 -0
- package/dist/renderer/context.d.ts +27 -7
- package/dist/renderer/context.d.ts.map +1 -1
- package/dist/renderer/html-builder.d.ts +10 -1
- package/dist/renderer/html-builder.d.ts.map +1 -1
- package/dist/renderer/html-builder.js +58 -5
- package/dist/renderer/llms.d.ts.map +1 -1
- package/dist/renderer/llms.js +133 -24
- package/dist/site-assembly.d.ts +25 -0
- package/dist/site-assembly.d.ts.map +1 -0
- package/dist/site-assembly.js +467 -0
- package/dist/site-url.d.ts +12 -0
- package/dist/site-url.d.ts.map +1 -0
- package/dist/site-url.js +67 -0
- package/dist/themes/default/sourcey.css +237 -1
- package/dist/utils/code-samples.d.ts.map +1 -1
- package/dist/utils/code-samples.js +53 -12
- package/dist/utils/markdown.d.ts +6 -0
- package/dist/utils/markdown.d.ts.map +1 -1
- package/dist/utils/markdown.js +11 -2
- package/dist/vite-plugin.d.ts +2 -0
- package/dist/vite-plugin.d.ts.map +1 -1
- package/dist/vite-plugin.js +6 -2
- package/package.json +6 -3
- package/dist/cli.js.map +0 -1
- package/dist/client/copy.js +0 -21
- package/dist/client/index.js.map +0 -1
- package/dist/components/App.js.map +0 -1
- package/dist/components/layout/Head.js.map +0 -1
- package/dist/components/layout/Header.js.map +0 -1
- package/dist/components/layout/Page.js.map +0 -1
- package/dist/components/layout/Sidebar.js.map +0 -1
- package/dist/components/layout/TableOfContents.js.map +0 -1
- package/dist/components/openapi/CodeSamples.js.map +0 -1
- package/dist/components/openapi/Definition.js.map +0 -1
- package/dist/components/openapi/EndpointBar.js.map +0 -1
- package/dist/components/openapi/Introduction.js.map +0 -1
- package/dist/components/openapi/Operation.js.map +0 -1
- package/dist/components/openapi/Parameters.js.map +0 -1
- package/dist/components/openapi/RequestBody.js.map +0 -1
- package/dist/components/openapi/Responses.js.map +0 -1
- package/dist/components/openapi/Security.js.map +0 -1
- package/dist/components/openapi/Tags.js.map +0 -1
- package/dist/components/schema/ExampleView.js.map +0 -1
- package/dist/components/schema/SchemaDatatype.js.map +0 -1
- package/dist/components/schema/SchemaView.js.map +0 -1
- package/dist/components/ui/Badge.js.map +0 -1
- package/dist/components/ui/CopyButton.js.map +0 -1
- package/dist/components/ui/Logo.js.map +0 -1
- package/dist/components/ui/Markdown.js.map +0 -1
- package/dist/components/ui/SectionLabel.js.map +0 -1
- package/dist/components/ui/SocialIcon.js.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/core/converter.js.map +0 -1
- package/dist/core/doxygen-loader.js.map +0 -1
- package/dist/core/loader.js.map +0 -1
- package/dist/core/markdown-loader.js.map +0 -1
- package/dist/core/navigation.js.map +0 -1
- package/dist/core/normalizer.js.map +0 -1
- package/dist/core/parser.js.map +0 -1
- package/dist/core/search-indexer.js.map +0 -1
- package/dist/core/types.js.map +0 -1
- package/dist/dev-server.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/init.js.map +0 -1
- package/dist/renderer/context.js.map +0 -1
- package/dist/renderer/html-builder.js.map +0 -1
- package/dist/renderer/static-renderer.js.map +0 -1
- package/dist/utils/code-samples.js.map +0 -1
- package/dist/utils/copy-svg.js.map +0 -1
- package/dist/utils/example-generator.js.map +0 -1
- package/dist/utils/highlighter.js.map +0 -1
- package/dist/utils/html-id.js.map +0 -1
- package/dist/utils/http.js.map +0 -1
- package/dist/utils/icons.js.map +0 -1
- package/dist/utils/lang-icons.js.map +0 -1
- package/dist/utils/markdown.js.map +0 -1
- package/dist/vite-plugin.js.map +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { basename, extname, relative } from "node:path";
|
|
3
3
|
import { load as parseYaml } from "js-yaml";
|
|
4
|
+
import { normalizeChangelog } from "./changelog-normalizer.js";
|
|
4
5
|
import { htmlId } from "../utils/html-id.js";
|
|
5
6
|
import { renderIcon } from "../utils/icons.js";
|
|
6
7
|
import { renderCodeBlock, renderMarkdown, renderMarkdownInline, extractHeadings, } from "../utils/markdown.js";
|
|
@@ -215,31 +216,6 @@ function buildDirectiveAttrList(entries) {
|
|
|
215
216
|
.map(([key, value]) => `${key}="${escapeDirectiveAttr(value)}"`);
|
|
216
217
|
return attrs.length > 0 ? `{${attrs.join(" ")}}` : "";
|
|
217
218
|
}
|
|
218
|
-
function indentBlock(text, prefix) {
|
|
219
|
-
const lines = text.split("\n");
|
|
220
|
-
const result = [];
|
|
221
|
-
let fence = null;
|
|
222
|
-
for (const line of lines) {
|
|
223
|
-
if (fence) {
|
|
224
|
-
// Inside a fenced code block — don't indent
|
|
225
|
-
result.push(line);
|
|
226
|
-
if (closesFence(line, fence))
|
|
227
|
-
fence = null;
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
const nextFence = isFenceStart(line);
|
|
231
|
-
if (nextFence) {
|
|
232
|
-
// Fence opener — don't indent so markdown recognises it
|
|
233
|
-
fence = nextFence;
|
|
234
|
-
result.push(line);
|
|
235
|
-
}
|
|
236
|
-
else {
|
|
237
|
-
result.push(`${prefix}${line}`);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return result.join("\n");
|
|
242
|
-
}
|
|
243
219
|
function isWhitespaceOnlyText(node) {
|
|
244
220
|
return node.kind === "text" && node.value.trim() === "";
|
|
245
221
|
}
|
|
@@ -406,10 +382,17 @@ function collectChildComponents(children, expected) {
|
|
|
406
382
|
* indented JSX content (e.g. code fences inside <Step>) is handled correctly.
|
|
407
383
|
*/
|
|
408
384
|
function renderComponentChildrenToHtml(children) {
|
|
385
|
+
// Save outer protection maps — renderDirectiveMarkdown → preprocessComponents will overwrite them
|
|
386
|
+
const savedFenceBlocks = activeFenceBlocks;
|
|
387
|
+
const savedInlineSpans = activeInlineSpans;
|
|
409
388
|
let raw = renderParsedComponentNodes(children);
|
|
410
|
-
raw = restoreFencedCodeBlocks(raw,
|
|
411
|
-
raw = restoreInlineCodeSpans(raw,
|
|
412
|
-
|
|
389
|
+
raw = restoreFencedCodeBlocks(raw, savedFenceBlocks);
|
|
390
|
+
raw = restoreInlineCodeSpans(raw, savedInlineSpans);
|
|
391
|
+
const result = renderDirectiveMarkdown(dedent(raw));
|
|
392
|
+
// Restore outer maps for sibling components
|
|
393
|
+
activeFenceBlocks = savedFenceBlocks;
|
|
394
|
+
activeInlineSpans = savedInlineSpans;
|
|
395
|
+
return result;
|
|
413
396
|
}
|
|
414
397
|
/**
|
|
415
398
|
* Strip common leading whitespace from a block of text.
|
|
@@ -946,6 +929,47 @@ function preprocessDirectives(body) {
|
|
|
946
929
|
export async function loadMarkdownPage(filePath, slug) {
|
|
947
930
|
const raw = await readFile(filePath, "utf-8");
|
|
948
931
|
const { meta, body } = parseFrontmatter(raw);
|
|
932
|
+
return loadMarkdownPageFromBody(filePath, slug, body, meta);
|
|
933
|
+
}
|
|
934
|
+
export async function loadDocsPage(filePath, slug, options = {}) {
|
|
935
|
+
const raw = await readFile(filePath, "utf-8");
|
|
936
|
+
const { meta, body } = parseFrontmatter(raw);
|
|
937
|
+
if (shouldTreatAsChangelog(filePath, meta, options)) {
|
|
938
|
+
const sourcePath = relative(process.cwd(), filePath);
|
|
939
|
+
const changelog = normalizeChangelog(body, {
|
|
940
|
+
title: typeof meta.title === "string" ? meta.title : undefined,
|
|
941
|
+
description: typeof meta.description === "string" ? meta.description : undefined,
|
|
942
|
+
repoUrl: options.repoUrl,
|
|
943
|
+
});
|
|
944
|
+
const description = typeof meta.description === "string"
|
|
945
|
+
? meta.description
|
|
946
|
+
: changelog.description ?? "";
|
|
947
|
+
return {
|
|
948
|
+
kind: "changelog",
|
|
949
|
+
title: changelog.title,
|
|
950
|
+
description,
|
|
951
|
+
slug,
|
|
952
|
+
headings: changelog.versions.map((version) => ({
|
|
953
|
+
level: 2,
|
|
954
|
+
text: version.version ?? "Unreleased",
|
|
955
|
+
id: version.id,
|
|
956
|
+
})),
|
|
957
|
+
sourcePath,
|
|
958
|
+
editPath: sourcePath,
|
|
959
|
+
changelog,
|
|
960
|
+
rawBody: body,
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
return loadMarkdownPageFromBody(filePath, slug, body, meta);
|
|
964
|
+
}
|
|
965
|
+
function shouldTreatAsChangelog(filePath, meta, options) {
|
|
966
|
+
if (options.changelog === false)
|
|
967
|
+
return false;
|
|
968
|
+
if (typeof meta.layout === "string" && meta.layout.toLowerCase() === "changelog")
|
|
969
|
+
return true;
|
|
970
|
+
return basename(filePath).toLowerCase() === "changelog.md";
|
|
971
|
+
}
|
|
972
|
+
function loadMarkdownPageFromBody(filePath, slug, body, meta) {
|
|
949
973
|
resetDirectiveCounter();
|
|
950
974
|
const componentPreprocessed = preprocessComponents(body);
|
|
951
975
|
const preprocessed = preprocessDirectives(componentPreprocessed);
|
|
@@ -957,6 +981,7 @@ export async function loadMarkdownPage(filePath, slug) {
|
|
|
957
981
|
const cleanHtml = html.replace(/^\s*<h1[^>]*>(.*?)<\/h1>\s*/i, (_m, inner) => inner.replace(/<[^>]+>/g, "").trim() === title ? "" : _m);
|
|
958
982
|
const sourcePath = relative(process.cwd(), filePath);
|
|
959
983
|
return {
|
|
984
|
+
kind: "markdown",
|
|
960
985
|
title,
|
|
961
986
|
description,
|
|
962
987
|
slug,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-normalizer.d.ts","sourceRoot":"","sources":["../../src/core/mcp-normalizer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAoE,MAAM,YAAY,CAAC;AAC5G,OAAO,KAAK,EACV,cAAc,
|
|
1
|
+
{"version":3,"file":"mcp-normalizer.d.ts","sourceRoot":"","sources":["../../src/core/mcp-normalizer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAoE,MAAM,YAAY,CAAC;AAC5G,OAAO,KAAK,EACV,cAAc,EASf,MAAM,YAAY,CAAC;AAKpB,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,CA+C9D"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { NormalizedSpec } from "./types.js";
|
|
2
2
|
import type { ResolvedTab } from "../config.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { DocsPage } from "./markdown-loader.js";
|
|
4
4
|
export interface SiteNavigation {
|
|
5
5
|
tabs: SiteTab[];
|
|
6
6
|
activeTabSlug: string;
|
|
@@ -38,7 +38,7 @@ export declare function buildNavFromSpec(spec: NormalizedSpec, tabSlug: string):
|
|
|
38
38
|
* Build a navigation tab from markdown pages.
|
|
39
39
|
* Groups come from the config; items come from loaded page data.
|
|
40
40
|
*/
|
|
41
|
-
export declare function buildNavFromPages(tab: ResolvedTab, pagesByPath: Map<string,
|
|
41
|
+
export declare function buildNavFromPages(tab: ResolvedTab, pagesByPath: Map<string, DocsPage>): SiteTab;
|
|
42
42
|
/**
|
|
43
43
|
* Assemble a SiteNavigation from a list of tabs.
|
|
44
44
|
* Defaults to the first tab and first page as active.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/core/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/core/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAOrD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,MAAM,GACd,OAAO,CAiDT;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,WAAW,EAChB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,GACjC,OAAO,CA6BT;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,cAAc,CASnE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,cAAc,CAEhB"}
|
package/dist/core/navigation.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { tagNavigationLabel } from "./tag-utils.js";
|
|
1
2
|
import { tabPath } from "../config.js";
|
|
2
3
|
import { htmlId } from "../utils/html-id.js";
|
|
3
4
|
// ---------------------------------------------------------------------------
|
|
@@ -25,6 +26,7 @@ export function buildNavFromSpec(spec, tabSlug) {
|
|
|
25
26
|
}
|
|
26
27
|
groups.push({ label: "", items: introItems });
|
|
27
28
|
// Tag groups with operations
|
|
29
|
+
const tagsByName = new Map(spec.tags.map((tag) => [tag.name, tag]));
|
|
28
30
|
for (const tag of spec.tags) {
|
|
29
31
|
if (tag.hidden)
|
|
30
32
|
continue;
|
|
@@ -34,7 +36,9 @@ export function buildNavFromSpec(spec, tabSlug) {
|
|
|
34
36
|
id: `operation-${htmlId(op.path)}-${htmlId(op.method)}`,
|
|
35
37
|
method: op.method,
|
|
36
38
|
}));
|
|
37
|
-
|
|
39
|
+
if (!items.length)
|
|
40
|
+
continue;
|
|
41
|
+
groups.push({ label: tagNavigationLabel(tag, tagsByName), items });
|
|
38
42
|
}
|
|
39
43
|
// Models group
|
|
40
44
|
const schemaNames = Object.keys(spec.schemas);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalizer.d.ts","sourceRoot":"","sources":["../../src/core/normalizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,UAAU,EACV,cAAc,
|
|
1
|
+
{"version":3,"file":"normalizer.d.ts","sourceRoot":"","sources":["../../src/core/normalizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,UAAU,EACV,cAAc,EAsBf,MAAM,YAAY,CAAC;AAMpB;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,cAAc,CAoBhE"}
|
package/dist/core/normalizer.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const HTTP_METHODS = [
|
|
2
|
-
"get", "post", "put", "delete", "patch", "options", "head", "trace",
|
|
2
|
+
"get", "post", "put", "delete", "patch", "options", "head", "trace", "query",
|
|
3
3
|
];
|
|
4
4
|
/**
|
|
5
5
|
* Transform a fully dereferenced OpenAPI 3.x document into a NormalizedSpec.
|
|
@@ -29,6 +29,7 @@ function normalizeInfo(doc) {
|
|
|
29
29
|
const info = doc.info ?? {};
|
|
30
30
|
return {
|
|
31
31
|
title: str(info.title, "Untitled API"),
|
|
32
|
+
summary: optStr(info.summary),
|
|
32
33
|
version: str(info.version, "0.0.0"),
|
|
33
34
|
description: optStr(info.description),
|
|
34
35
|
termsOfService: optStr(info.termsOfService),
|
|
@@ -82,7 +83,10 @@ function normalizeTags(rawTags, operations) {
|
|
|
82
83
|
if (name) {
|
|
83
84
|
tagMap.set(name, {
|
|
84
85
|
name,
|
|
86
|
+
summary: optStr(t.summary),
|
|
85
87
|
description: optStr(t.description),
|
|
88
|
+
parent: optStr(t.parent),
|
|
89
|
+
kind: optStr(t.kind),
|
|
86
90
|
externalDocs: normalizeExternalDocs(t.externalDocs),
|
|
87
91
|
operations: [],
|
|
88
92
|
hidden: t["x-sourcey-hide"] === true,
|
|
@@ -106,10 +110,49 @@ function normalizeTags(rawTags, operations) {
|
|
|
106
110
|
tagMap.get(tagName).operations.push(op);
|
|
107
111
|
}
|
|
108
112
|
}
|
|
109
|
-
|
|
110
|
-
|
|
113
|
+
const referencedAsParent = new Set();
|
|
114
|
+
for (const tag of tagMap.values()) {
|
|
115
|
+
if (tag.parent && tagMap.has(tag.parent)) {
|
|
116
|
+
referencedAsParent.add(tag.parent);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Keep tags that contribute visible metadata or structure, even without operations.
|
|
120
|
+
return sortTagsByHierarchy(Array.from(tagMap.values()).filter((t) => t.operations.length > 0
|
|
121
|
+
|| !!t.summary
|
|
122
|
+
|| !!t.description
|
|
123
|
+
|| !!t.kind
|
|
124
|
+
|| referencedAsParent.has(t.name)));
|
|
125
|
+
}
|
|
126
|
+
function sortTagsByHierarchy(tags) {
|
|
127
|
+
const tagsByName = new Map(tags.map((tag) => [tag.name, tag]));
|
|
128
|
+
const childrenByParent = new Map();
|
|
129
|
+
const roots = [];
|
|
130
|
+
for (const tag of tags) {
|
|
131
|
+
if (tag.parent && tag.parent !== tag.name && tagsByName.has(tag.parent)) {
|
|
132
|
+
const siblings = childrenByParent.get(tag.parent) ?? [];
|
|
133
|
+
siblings.push(tag);
|
|
134
|
+
childrenByParent.set(tag.parent, siblings);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
roots.push(tag);
|
|
138
|
+
}
|
|
139
|
+
const sorted = [];
|
|
140
|
+
const visited = new Set();
|
|
141
|
+
const visit = (tag) => {
|
|
142
|
+
if (visited.has(tag.name))
|
|
143
|
+
return;
|
|
144
|
+
visited.add(tag.name);
|
|
145
|
+
sorted.push(tag);
|
|
146
|
+
for (const child of childrenByParent.get(tag.name) ?? []) {
|
|
147
|
+
visit(child);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
for (const tag of roots)
|
|
151
|
+
visit(tag);
|
|
152
|
+
for (const tag of tags)
|
|
153
|
+
visit(tag);
|
|
154
|
+
return sorted;
|
|
111
155
|
}
|
|
112
|
-
// ── Operations ─────────────────────────────────────────────────────
|
|
113
156
|
function normalizeOperations(paths, globalSecurity) {
|
|
114
157
|
if (!paths)
|
|
115
158
|
return [];
|
|
@@ -173,6 +216,9 @@ function normalizeParameters(params) {
|
|
|
173
216
|
schema: p.schema
|
|
174
217
|
? normalizeSchema(p.schema)
|
|
175
218
|
: undefined,
|
|
219
|
+
content: p.content
|
|
220
|
+
? normalizeContent(p.content)
|
|
221
|
+
: undefined,
|
|
176
222
|
example: p.example,
|
|
177
223
|
examples: p.examples
|
|
178
224
|
? normalizeExamples(p.examples)
|
|
@@ -201,15 +247,57 @@ function normalizeContent(content) {
|
|
|
201
247
|
examples: value.examples
|
|
202
248
|
? normalizeExamples(value.examples)
|
|
203
249
|
: undefined,
|
|
250
|
+
encoding: isRecord(value.encoding)
|
|
251
|
+
? normalizeEncodingMap(value.encoding)
|
|
252
|
+
: undefined,
|
|
253
|
+
prefixEncoding: Array.isArray(value.prefixEncoding)
|
|
254
|
+
? value.prefixEncoding
|
|
255
|
+
.filter(isRecord)
|
|
256
|
+
.map((entry) => normalizeEncodingObject(entry))
|
|
257
|
+
: undefined,
|
|
258
|
+
itemEncoding: isRecord(value.itemEncoding)
|
|
259
|
+
? normalizeEncodingObject(value.itemEncoding)
|
|
260
|
+
: undefined,
|
|
204
261
|
};
|
|
205
262
|
}
|
|
206
263
|
return result;
|
|
207
264
|
}
|
|
265
|
+
function normalizeEncodingMap(encodings) {
|
|
266
|
+
const result = {};
|
|
267
|
+
for (const [name, encoding] of Object.entries(encodings)) {
|
|
268
|
+
if (!isRecord(encoding))
|
|
269
|
+
continue;
|
|
270
|
+
result[name] = normalizeEncodingObject(encoding);
|
|
271
|
+
}
|
|
272
|
+
return result;
|
|
273
|
+
}
|
|
274
|
+
function normalizeEncodingObject(encoding) {
|
|
275
|
+
return {
|
|
276
|
+
contentType: optStr(encoding.contentType),
|
|
277
|
+
headers: isRecord(encoding.headers)
|
|
278
|
+
? normalizeResponseHeaders(encoding.headers)
|
|
279
|
+
: undefined,
|
|
280
|
+
style: optStr(encoding.style),
|
|
281
|
+
explode: typeof encoding.explode === "boolean" ? encoding.explode : undefined,
|
|
282
|
+
allowReserved: typeof encoding.allowReserved === "boolean"
|
|
283
|
+
? encoding.allowReserved
|
|
284
|
+
: undefined,
|
|
285
|
+
prefixEncoding: Array.isArray(encoding.prefixEncoding)
|
|
286
|
+
? encoding.prefixEncoding
|
|
287
|
+
.filter(isRecord)
|
|
288
|
+
.map((entry) => normalizeEncodingObject(entry))
|
|
289
|
+
: undefined,
|
|
290
|
+
itemEncoding: isRecord(encoding.itemEncoding)
|
|
291
|
+
? normalizeEncodingObject(encoding.itemEncoding)
|
|
292
|
+
: undefined,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
208
295
|
function normalizeResponses(responses) {
|
|
209
296
|
if (!responses)
|
|
210
297
|
return [];
|
|
211
298
|
return Object.entries(responses).map(([statusCode, r]) => ({
|
|
212
299
|
statusCode,
|
|
300
|
+
summary: optStr(r.summary),
|
|
213
301
|
description: str(r.description, ""),
|
|
214
302
|
content: r.content
|
|
215
303
|
? normalizeContent(r.content)
|
|
@@ -234,6 +322,9 @@ function normalizeResponseHeaders(headers) {
|
|
|
234
322
|
schema: h.schema
|
|
235
323
|
? normalizeSchema(h.schema)
|
|
236
324
|
: undefined,
|
|
325
|
+
content: h.content
|
|
326
|
+
? normalizeContent(h.content)
|
|
327
|
+
: undefined,
|
|
237
328
|
};
|
|
238
329
|
}
|
|
239
330
|
return result;
|
|
@@ -409,6 +500,8 @@ function normalizeSecuritySchemes(schemes) {
|
|
|
409
500
|
? normalizeOAuthFlows(raw.flows)
|
|
410
501
|
: undefined,
|
|
411
502
|
openIdConnectUrl: optStr(raw.openIdConnectUrl),
|
|
503
|
+
oauth2MetadataUrl: optStr(raw.oauth2MetadataUrl),
|
|
504
|
+
deprecated: raw.deprecated === true,
|
|
412
505
|
};
|
|
413
506
|
}
|
|
414
507
|
return result;
|
|
@@ -420,6 +513,7 @@ function normalizeOAuthFlows(flows) {
|
|
|
420
513
|
"password",
|
|
421
514
|
"clientCredentials",
|
|
422
515
|
"authorizationCode",
|
|
516
|
+
"deviceAuthorization",
|
|
423
517
|
]) {
|
|
424
518
|
if (flows[flowType]) {
|
|
425
519
|
result[flowType] = normalizeOAuthFlow(flows[flowType]);
|
|
@@ -430,6 +524,7 @@ function normalizeOAuthFlows(flows) {
|
|
|
430
524
|
function normalizeOAuthFlow(flow) {
|
|
431
525
|
return {
|
|
432
526
|
authorizationUrl: optStr(flow.authorizationUrl),
|
|
527
|
+
deviceAuthorizationUrl: optStr(flow.deviceAuthorizationUrl),
|
|
433
528
|
tokenUrl: optStr(flow.tokenUrl),
|
|
434
529
|
refreshUrl: optStr(flow.refreshUrl),
|
|
435
530
|
scopes: (flow.scopes ?? {}),
|
|
@@ -469,3 +564,6 @@ function str(value, fallback) {
|
|
|
469
564
|
function optStr(value) {
|
|
470
565
|
return typeof value === "string" ? value : undefined;
|
|
471
566
|
}
|
|
567
|
+
function isRecord(value) {
|
|
568
|
+
return typeof value === "object" && value !== null;
|
|
569
|
+
}
|
package/dist/core/parser.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ import type { LoadedSpec, ParsedSpec } from "./types.js";
|
|
|
3
3
|
* Dereference a loaded spec document.
|
|
4
4
|
* All $ref pointers are resolved (local, remote, circular).
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Uses the document source as the base URI, but honors OpenAPI 3.2's
|
|
7
|
+
* `$self` field when present so relative references resolve canonically.
|
|
8
8
|
*/
|
|
9
9
|
export declare function parseSpec(loaded: LoadedSpec): Promise<ParsedSpec>;
|
|
10
10
|
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/core/parser.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/core/parser.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAmB,UAAU,EAAE,MAAM,YAAY,CAAC;AAE1E;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAiBvE"}
|
package/dist/core/parser.js
CHANGED
|
@@ -1,26 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $RefParser } from "@apidevtools/json-schema-ref-parser";
|
|
2
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
2
3
|
/**
|
|
3
4
|
* Dereference a loaded spec document.
|
|
4
5
|
* All $ref pointers are resolved (local, remote, circular).
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
7
|
+
* Uses the document source as the base URI, but honors OpenAPI 3.2's
|
|
8
|
+
* `$self` field when present so relative references resolve canonically.
|
|
8
9
|
*/
|
|
9
10
|
export async function parseSpec(loaded) {
|
|
10
11
|
try {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
if (isLocalFile) {
|
|
14
|
-
// Dereference from file path so relative $refs resolve correctly
|
|
15
|
-
dereferenced = await dereference(loaded.source, {
|
|
16
|
-
dereference: { circular: "ignore" },
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
else {
|
|
20
|
-
dereferenced = await dereference(structuredClone(loaded.raw), {
|
|
21
|
-
dereference: { circular: "ignore" },
|
|
22
|
-
});
|
|
23
|
-
}
|
|
12
|
+
const parser = new $RefParser();
|
|
13
|
+
const dereferenced = await parser.dereference(resolveDocumentUri(loaded), structuredClone(loaded.raw), { dereference: { circular: "ignore" } });
|
|
24
14
|
return {
|
|
25
15
|
document: dereferenced,
|
|
26
16
|
source: loaded.source,
|
|
@@ -31,3 +21,20 @@ export async function parseSpec(loaded) {
|
|
|
31
21
|
throw new Error(`Failed to parse OpenAPI spec: ${message}`);
|
|
32
22
|
}
|
|
33
23
|
}
|
|
24
|
+
function resolveDocumentUri(loaded) {
|
|
25
|
+
const self = typeof loaded.raw["$self"] === "string" ? loaded.raw["$self"] : undefined;
|
|
26
|
+
if (!self)
|
|
27
|
+
return loaded.source;
|
|
28
|
+
try {
|
|
29
|
+
if (loaded.source.startsWith("http://") || loaded.source.startsWith("https://")) {
|
|
30
|
+
return new URL(self, loaded.source).toString();
|
|
31
|
+
}
|
|
32
|
+
const resolved = new URL(self, pathToFileURL(loaded.source));
|
|
33
|
+
return resolved.protocol === "file:"
|
|
34
|
+
? fileURLToPath(resolved)
|
|
35
|
+
: resolved.toString();
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return loaded.source;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { NormalizedSpec } from "./types.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { DocsPage } from "./markdown-loader.js";
|
|
3
3
|
import type { SiteNavigation } from "./navigation.js";
|
|
4
|
+
import type { PrettyUrls } from "../site-url.js";
|
|
4
5
|
export interface SearchEntry {
|
|
5
6
|
/** Display title */
|
|
6
7
|
title: string;
|
|
@@ -23,5 +24,5 @@ export interface SearchEntry {
|
|
|
23
24
|
* Build a search index from specs and markdown pages.
|
|
24
25
|
* Returns a JSON string ready to write to disk.
|
|
25
26
|
*/
|
|
26
|
-
export declare function buildSearchIndex(specs: Map<string, NormalizedSpec>, pages: Map<string,
|
|
27
|
+
export declare function buildSearchIndex(specs: Map<string, NormalizedSpec>, pages: Map<string, DocsPage[]>, navigation: SiteNavigation, assetBase?: string, featuredSlugs?: string[], prettyUrls?: PrettyUrls): string;
|
|
27
28
|
//# sourceMappingURL=search-indexer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-indexer.d.ts","sourceRoot":"","sources":["../../src/core/search-indexer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"search-indexer.d.ts","sourceRoot":"","sources":["../../src/core/search-indexer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAOjD,MAAM,WAAW,WAAW;IAC1B,oBAAoB;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAMD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,EAClC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,EAC9B,UAAU,EAAE,cAAc,EAC1B,SAAS,SAAM,EACf,aAAa,GAAE,MAAM,EAAO,EAC5B,UAAU,GAAE,UAAkB,GAC7B,MAAM,CAuFR"}
|
|
@@ -6,21 +6,29 @@ import { htmlId } from "../utils/html-id.js";
|
|
|
6
6
|
* Build a search index from specs and markdown pages.
|
|
7
7
|
* Returns a JSON string ready to write to disk.
|
|
8
8
|
*/
|
|
9
|
-
export function buildSearchIndex(specs, pages, navigation, assetBase = "/", featuredSlugs = []) {
|
|
9
|
+
export function buildSearchIndex(specs, pages, navigation, assetBase = "/", featuredSlugs = [], prettyUrls = false) {
|
|
10
10
|
const base = assetBase.endsWith("/") ? assetBase : assetBase + "/";
|
|
11
11
|
const featuredSet = new Set(featuredSlugs);
|
|
12
12
|
const entries = [];
|
|
13
|
+
const pageUrl = (tabSlug, slug) => {
|
|
14
|
+
const tabPrefix = tabSlug ? `${base}${tabSlug}/` : base;
|
|
15
|
+
if (prettyUrls === "strip")
|
|
16
|
+
return `${tabPrefix}${slug}`;
|
|
17
|
+
if (prettyUrls === "slash")
|
|
18
|
+
return `${tabPrefix}${slug}/`;
|
|
19
|
+
return `${tabPrefix}${slug}.html`;
|
|
20
|
+
};
|
|
13
21
|
// Index OpenAPI specs
|
|
14
22
|
for (const [tabSlug, spec] of specs) {
|
|
15
23
|
const tab = navigation.tabs.find((t) => t.slug === tabSlug);
|
|
16
24
|
const tabLabel = tab?.label ?? tabSlug;
|
|
17
|
-
const
|
|
25
|
+
const tabBase = tabSlug ? `${base}${tabSlug}/` : base;
|
|
18
26
|
// Operations
|
|
19
27
|
for (const op of spec.operations) {
|
|
20
28
|
entries.push({
|
|
21
29
|
title: op.summary ?? `${op.method.toUpperCase()} ${op.path}`,
|
|
22
30
|
content: op.description?.slice(0, 200) ?? "",
|
|
23
|
-
url: `${
|
|
31
|
+
url: `${tabBase}#operation-${htmlId(op.path)}-${htmlId(op.method)}`,
|
|
24
32
|
method: op.method,
|
|
25
33
|
path: op.path,
|
|
26
34
|
tab: tabLabel,
|
|
@@ -32,7 +40,7 @@ export function buildSearchIndex(specs, pages, navigation, assetBase = "/", feat
|
|
|
32
40
|
entries.push({
|
|
33
41
|
title: name,
|
|
34
42
|
content: spec.schemas[name].description?.slice(0, 200) ?? "",
|
|
35
|
-
url: `${
|
|
43
|
+
url: `${tabBase}#definition-${htmlId(name)}`,
|
|
36
44
|
tab: tabLabel,
|
|
37
45
|
category: "Models",
|
|
38
46
|
});
|
|
@@ -43,26 +51,39 @@ export function buildSearchIndex(specs, pages, navigation, assetBase = "/", feat
|
|
|
43
51
|
const tab = navigation.tabs.find((t) => t.slug === tabSlug);
|
|
44
52
|
const tabLabel = tab?.label ?? tabSlug;
|
|
45
53
|
for (const page of tabPages) {
|
|
46
|
-
// Page itself
|
|
47
|
-
const pageBase = tabSlug ? `${base}${tabSlug}/` : base;
|
|
48
54
|
const isFeatured = featuredSet.has(page.slug);
|
|
55
|
+
const href = pageUrl(tabSlug, page.slug);
|
|
49
56
|
entries.push({
|
|
50
57
|
title: page.title,
|
|
51
|
-
content: page.
|
|
52
|
-
|
|
58
|
+
content: page.kind === "markdown"
|
|
59
|
+
? page.description || stripHtml(page.html).slice(0, 200)
|
|
60
|
+
: page.description || summarizeChangelog(page).slice(0, 200),
|
|
61
|
+
url: href,
|
|
53
62
|
tab: tabLabel,
|
|
54
63
|
category: "Pages",
|
|
55
64
|
...(isFeatured && { featured: true }),
|
|
56
65
|
});
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
if (page.kind === "markdown") {
|
|
67
|
+
for (const heading of page.headings) {
|
|
68
|
+
entries.push({
|
|
69
|
+
title: heading.text,
|
|
70
|
+
content: `${page.title} — ${heading.text}`,
|
|
71
|
+
url: `${href}#${heading.id}`,
|
|
72
|
+
tab: tabLabel,
|
|
73
|
+
category: "Sections",
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
for (const version of page.changelog.versions) {
|
|
79
|
+
entries.push({
|
|
80
|
+
title: version.version ?? "Unreleased",
|
|
81
|
+
content: summarizeVersion(version).slice(0, 200),
|
|
82
|
+
url: `${href}#${version.id}`,
|
|
83
|
+
tab: tabLabel,
|
|
84
|
+
category: "Releases",
|
|
85
|
+
});
|
|
86
|
+
}
|
|
66
87
|
}
|
|
67
88
|
}
|
|
68
89
|
}
|
|
@@ -74,3 +95,15 @@ export function buildSearchIndex(specs, pages, navigation, assetBase = "/", feat
|
|
|
74
95
|
function stripHtml(html) {
|
|
75
96
|
return html.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
76
97
|
}
|
|
98
|
+
function summarizeChangelog(page) {
|
|
99
|
+
return page.changelog.versions
|
|
100
|
+
.slice(0, 3)
|
|
101
|
+
.map(summarizeVersion)
|
|
102
|
+
.join(" ");
|
|
103
|
+
}
|
|
104
|
+
function summarizeVersion(version) {
|
|
105
|
+
if (version.summary)
|
|
106
|
+
return version.summary;
|
|
107
|
+
const entries = version.sections.flatMap((section) => section.entries.map((entry) => entry.text));
|
|
108
|
+
return entries.slice(0, 3).join(" ");
|
|
109
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { NormalizedTag } from "./types.js";
|
|
2
|
+
export declare function tagDisplayName(tag: Pick<NormalizedTag, "name" | "summary">): string;
|
|
3
|
+
export declare function tagLineage(tag: Pick<NormalizedTag, "name" | "parent">, tagsByName: Map<string, NormalizedTag>): NormalizedTag[];
|
|
4
|
+
export declare function tagNavigationLabel(tag: NormalizedTag, tagsByName: Map<string, NormalizedTag>): string;
|
|
5
|
+
//# sourceMappingURL=tag-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tag-utils.d.ts","sourceRoot":"","sources":["../../src/core/tag-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,wBAAgB,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,CAEnF;AAED,wBAAgB,UAAU,CACxB,GAAG,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,QAAQ,CAAC,EAC3C,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GACrC,aAAa,EAAE,CAcjB;AAED,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,aAAa,EAClB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GACrC,MAAM,CAIR"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function tagDisplayName(tag) {
|
|
2
|
+
return tag.summary ?? tag.name;
|
|
3
|
+
}
|
|
4
|
+
export function tagLineage(tag, tagsByName) {
|
|
5
|
+
const lineage = [];
|
|
6
|
+
const seen = new Set([tag.name]);
|
|
7
|
+
let parentName = tag.parent;
|
|
8
|
+
while (parentName && !seen.has(parentName)) {
|
|
9
|
+
const parent = tagsByName.get(parentName);
|
|
10
|
+
if (!parent)
|
|
11
|
+
break;
|
|
12
|
+
lineage.unshift(parent);
|
|
13
|
+
seen.add(parent.name);
|
|
14
|
+
parentName = parent.parent;
|
|
15
|
+
}
|
|
16
|
+
return lineage;
|
|
17
|
+
}
|
|
18
|
+
export function tagNavigationLabel(tag, tagsByName) {
|
|
19
|
+
return [...tagLineage(tag, tagsByName), tag]
|
|
20
|
+
.map(tagDisplayName)
|
|
21
|
+
.join(" / ");
|
|
22
|
+
}
|