@dogsbay/format-fluidtopics-rest 0.2.0-beta.92
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 +87 -0
- package/dist/assets.d.ts +13 -0
- package/dist/assets.d.ts.map +1 -0
- package/dist/assets.js +53 -0
- package/dist/assets.js.map +1 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +28 -0
- package/dist/cli.js.map +1 -0
- package/dist/frontmatter.d.ts +15 -0
- package/dist/frontmatter.d.ts.map +1 -0
- package/dist/frontmatter.js +24 -0
- package/dist/frontmatter.js.map +1 -0
- package/dist/headings.d.ts +13 -0
- package/dist/headings.d.ts.map +1 -0
- package/dist/headings.js +76 -0
- package/dist/headings.js.map +1 -0
- package/dist/html-to-tree.d.ts +11 -0
- package/dist/html-to-tree.d.ts.map +1 -0
- package/dist/html-to-tree.js +285 -0
- package/dist/html-to-tree.js.map +1 -0
- package/dist/importer.d.ts +44 -0
- package/dist/importer.d.ts.map +1 -0
- package/dist/importer.js +196 -0
- package/dist/importer.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/inline-html.d.ts +52 -0
- package/dist/inline-html.d.ts.map +1 -0
- package/dist/inline-html.js +157 -0
- package/dist/inline-html.js.map +1 -0
- package/dist/khub.d.ts +59 -0
- package/dist/khub.d.ts.map +1 -0
- package/dist/khub.js +93 -0
- package/dist/khub.js.map +1 -0
- package/dist/links.d.ts +13 -0
- package/dist/links.d.ts.map +1 -0
- package/dist/links.js +55 -0
- package/dist/links.js.map +1 -0
- package/dist/nav.d.ts +7 -0
- package/dist/nav.d.ts.map +1 -0
- package/dist/nav.js +15 -0
- package/dist/nav.js.map +1 -0
- package/dist/single-page.d.ts +47 -0
- package/dist/single-page.d.ts.map +1 -0
- package/dist/single-page.js +77 -0
- package/dist/single-page.js.map +1 -0
- package/dist/tree.d.ts +2 -0
- package/dist/tree.d.ts.map +1 -0
- package/dist/tree.js +2 -0
- package/dist/tree.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert DITA-OT HTML5 *inline* content (a cheerio/domhandler node list) into
|
|
3
|
+
* Dogsbay InlineNode[] (flat-annotation / Notion model).
|
|
4
|
+
*
|
|
5
|
+
* DITA-OT emits inline DITA elements as classed spans/anchors:
|
|
6
|
+
* <span class="ph">…</span> → unwrapped (DITA <ph>)
|
|
7
|
+
* <span class="keyword|term|uicontrol">→ unwrapped to text
|
|
8
|
+
* <span class="codeph">…</span> → inline code (monospace domain)
|
|
9
|
+
* <a class="xref|link" href>…</a> → link
|
|
10
|
+
* <strong>/<b>, <em>/<i> → bold / italic annotations
|
|
11
|
+
* <img> → image
|
|
12
|
+
* FT also wraps text in <span class="sentence"> (translation segmentation) and
|
|
13
|
+
* emotion's <span class="css-…">; both are unwrapped.
|
|
14
|
+
*/
|
|
15
|
+
import type { InlineNode } from "@dogsbay/types";
|
|
16
|
+
/** Minimal structural view of a domhandler/cheerio node. */
|
|
17
|
+
export interface DomNode {
|
|
18
|
+
type: string;
|
|
19
|
+
name?: string;
|
|
20
|
+
data?: string;
|
|
21
|
+
attribs?: Record<string, string>;
|
|
22
|
+
children?: DomNode[];
|
|
23
|
+
}
|
|
24
|
+
export interface InlineOptions {
|
|
25
|
+
/** Rewrites an href; defaults to identity. */
|
|
26
|
+
resolveHref?: (href: string) => string;
|
|
27
|
+
/** Rewrites an image src; defaults to identity. */
|
|
28
|
+
resolveSrc?: (src: string) => string;
|
|
29
|
+
/**
|
|
30
|
+
* Rich image hook — receives the FT image metadata (incl. `data:` src,
|
|
31
|
+
* `data-ft-asset-display-name`, `data-ft-resource-id`) and returns the src to
|
|
32
|
+
* use. Lets the importer extract `data:` images to real `_assets` files.
|
|
33
|
+
* Takes precedence over `resolveSrc`.
|
|
34
|
+
*/
|
|
35
|
+
onImage?: (img: {
|
|
36
|
+
src: string;
|
|
37
|
+
alt?: string;
|
|
38
|
+
displayName?: string;
|
|
39
|
+
resourceId?: string;
|
|
40
|
+
}) => string;
|
|
41
|
+
/**
|
|
42
|
+
* Resolve an FT internal cross-reference (`<a class="ft-internal-link"
|
|
43
|
+
* data-tocid=… data-mapid=…>` — hrefless, resolved by FT's client at
|
|
44
|
+
* runtime) to a route. Return undefined to leave it as plain text.
|
|
45
|
+
*/
|
|
46
|
+
resolveTocId?: (tocId: string, mapId?: string) => string | undefined;
|
|
47
|
+
}
|
|
48
|
+
/** Convert a node's children into InlineNode[]. */
|
|
49
|
+
export declare function inlineFromChildren(nodes: DomNode[] | undefined, opts?: InlineOptions): InlineNode[];
|
|
50
|
+
/** Plain text of a node and its descendants (whitespace-collapsed). */
|
|
51
|
+
export declare function textContent(n: DomNode): string;
|
|
52
|
+
//# sourceMappingURL=inline-html.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-html.d.ts","sourceRoot":"","sources":["../src/inline-html.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,gBAAgB,CAAC;AAE7D,4DAA4D;AAC5D,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;CACtB;AAcD,MAAM,WAAW,aAAa;IAC5B,8CAA8C;IAC9C,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACvC,mDAAmD;IACnD,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IACrC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,CAAC;IACpG;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CACtE;AAED,mDAAmD;AACnD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,SAAS,EAAE,IAAI,GAAE,aAAkB,GAAG,UAAU,EAAE,CAIvG;AA0GD,uEAAuE;AACvE,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAU9C"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// Monospace (code) DITA inline domains. NB: `cmd` is deliberately excluded — in
|
|
2
|
+
// a task, <span class="ph cmd"> is the *step command* (an instruction sentence,
|
|
3
|
+
// i.e. prose), not code. `cmdname` (a literal command name) stays; the word
|
|
4
|
+
// boundary means it never matches the `cmd` step class.
|
|
5
|
+
const CODE_CLASSES = /\b(codeph|cmdname|filepath|userinput|systemoutput|parmname|varname|option|kbd|synph)\b/;
|
|
6
|
+
/** Convert a node's children into InlineNode[]. */
|
|
7
|
+
export function inlineFromChildren(nodes, opts = {}) {
|
|
8
|
+
const out = [];
|
|
9
|
+
walk(nodes ?? [], { bold: false, italic: false, strike: false }, out, opts);
|
|
10
|
+
return trimEdges(out);
|
|
11
|
+
}
|
|
12
|
+
function classOf(n) {
|
|
13
|
+
return n.attribs?.class ?? "";
|
|
14
|
+
}
|
|
15
|
+
function walk(nodes, ann, out, opts) {
|
|
16
|
+
for (const n of nodes) {
|
|
17
|
+
if (n.type === "text") {
|
|
18
|
+
const text = (n.data ?? "").replace(/\s+/g, " ");
|
|
19
|
+
if (text)
|
|
20
|
+
out.push(makeText(text, ann));
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (n.type !== "tag")
|
|
24
|
+
continue; // skip comments, etc.
|
|
25
|
+
const name = (n.name ?? "").toLowerCase();
|
|
26
|
+
const cls = classOf(n);
|
|
27
|
+
switch (name) {
|
|
28
|
+
case "strong":
|
|
29
|
+
case "b":
|
|
30
|
+
walk(n.children ?? [], { ...ann, bold: true }, out, opts);
|
|
31
|
+
break;
|
|
32
|
+
case "em":
|
|
33
|
+
case "i":
|
|
34
|
+
walk(n.children ?? [], { ...ann, italic: true }, out, opts);
|
|
35
|
+
break;
|
|
36
|
+
case "s":
|
|
37
|
+
case "del":
|
|
38
|
+
case "strike":
|
|
39
|
+
walk(n.children ?? [], { ...ann, strike: true }, out, opts);
|
|
40
|
+
break;
|
|
41
|
+
case "code":
|
|
42
|
+
out.push({ type: "code", text: textContent(n) });
|
|
43
|
+
break;
|
|
44
|
+
case "a": {
|
|
45
|
+
const rawHref = n.attribs?.href ?? "";
|
|
46
|
+
let href;
|
|
47
|
+
if (rawHref) {
|
|
48
|
+
href = (opts.resolveHref ?? ((h) => h))(rawHref);
|
|
49
|
+
}
|
|
50
|
+
else if (opts.resolveTocId && n.attribs?.["data-tocid"]) {
|
|
51
|
+
// FT internal xref: hrefless <a data-tocid data-mapid>.
|
|
52
|
+
href = opts.resolveTocId(n.attribs["data-tocid"], n.attribs["data-mapid"]);
|
|
53
|
+
}
|
|
54
|
+
const children = [];
|
|
55
|
+
walk(n.children ?? [], ann, children, opts);
|
|
56
|
+
const kids = children.length ? children : [{ type: "text", text: textContent(n) }];
|
|
57
|
+
if (href) {
|
|
58
|
+
out.push({ type: "link", href, children: kids });
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
// Unresolvable / hrefless → emit the text, not a dead empty link.
|
|
62
|
+
out.push(...kids);
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
case "img": {
|
|
67
|
+
const rawSrc = n.attribs?.src ?? "";
|
|
68
|
+
const displayName = n.attribs?.["data-ft-asset-display-name"];
|
|
69
|
+
const resourceId = n.attribs?.["data-ft-resource-id"];
|
|
70
|
+
// alt: explicit alt, else the FT display-name (filename) for a11y.
|
|
71
|
+
const alt = n.attribs?.alt || displayName || undefined;
|
|
72
|
+
let src = rawSrc;
|
|
73
|
+
if (opts.onImage)
|
|
74
|
+
src = opts.onImage({ src: rawSrc, alt, displayName, resourceId });
|
|
75
|
+
else if (opts.resolveSrc)
|
|
76
|
+
src = opts.resolveSrc(rawSrc);
|
|
77
|
+
const img = { type: "image", src };
|
|
78
|
+
if (alt)
|
|
79
|
+
img.alt = alt;
|
|
80
|
+
out.push(img);
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
case "br":
|
|
84
|
+
out.push({ type: "break" });
|
|
85
|
+
break;
|
|
86
|
+
case "span": {
|
|
87
|
+
const tocid = n.attribs?.["data-tocid"];
|
|
88
|
+
if (CODE_CLASSES.test(cls)) {
|
|
89
|
+
out.push({ type: "code", text: textContent(n) });
|
|
90
|
+
}
|
|
91
|
+
else if (tocid && opts.resolveTocId) {
|
|
92
|
+
// FT internal xref rendered as a hrefless span:
|
|
93
|
+
// <span class="xref ft-internal-link" data-tocid data-mapid>text</span>
|
|
94
|
+
const href = opts.resolveTocId(tocid, n.attribs?.["data-mapid"]);
|
|
95
|
+
const children = [];
|
|
96
|
+
walk(n.children ?? [], ann, children, opts);
|
|
97
|
+
const kids = children.length ? children : [{ type: "text", text: textContent(n) }];
|
|
98
|
+
if (href)
|
|
99
|
+
out.push({ type: "link", href, children: kids });
|
|
100
|
+
else
|
|
101
|
+
out.push(...kids);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// ph / keyword / term / uicontrol / sentence / css-… → unwrap
|
|
105
|
+
walk(n.children ?? [], ann, out, opts);
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
default:
|
|
110
|
+
// sup/sub/wbr/abbr/etc. → unwrap to their inline content
|
|
111
|
+
walk(n.children ?? [], ann, out, opts);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function makeText(text, ann) {
|
|
116
|
+
const span = { type: "text", text };
|
|
117
|
+
if (ann.bold)
|
|
118
|
+
span.bold = true;
|
|
119
|
+
if (ann.italic)
|
|
120
|
+
span.italic = true;
|
|
121
|
+
if (ann.strike)
|
|
122
|
+
span.strikethrough = true;
|
|
123
|
+
return span;
|
|
124
|
+
}
|
|
125
|
+
/** Plain text of a node and its descendants (whitespace-collapsed). */
|
|
126
|
+
export function textContent(n) {
|
|
127
|
+
let s = "";
|
|
128
|
+
const rec = (nodes) => {
|
|
129
|
+
for (const c of nodes) {
|
|
130
|
+
if (c.type === "text")
|
|
131
|
+
s += c.data ?? "";
|
|
132
|
+
else if (c.children)
|
|
133
|
+
rec(c.children);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
rec(n.children ?? []);
|
|
137
|
+
return s.replace(/\s+/g, " ").trim();
|
|
138
|
+
}
|
|
139
|
+
/** Trim leading/trailing whitespace-only text from the inline run. */
|
|
140
|
+
function trimEdges(nodes) {
|
|
141
|
+
const a = [...nodes];
|
|
142
|
+
while (a.length && a[0].type === "text" && a[0].text.trim() === "")
|
|
143
|
+
a.shift();
|
|
144
|
+
while (a.length && a[a.length - 1].type === "text" && a[a.length - 1].text.trim() === "")
|
|
145
|
+
a.pop();
|
|
146
|
+
// Trim the inner edges of the first/last text spans.
|
|
147
|
+
if (a.length) {
|
|
148
|
+
const first = a[0];
|
|
149
|
+
if (first.type === "text")
|
|
150
|
+
first.text = first.text.replace(/^\s+/, "");
|
|
151
|
+
const last = a[a.length - 1];
|
|
152
|
+
if (last.type === "text")
|
|
153
|
+
last.text = last.text.replace(/\s+$/, "");
|
|
154
|
+
}
|
|
155
|
+
return a;
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=inline-html.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-html.js","sourceRoot":"","sources":["../src/inline-html.ts"],"names":[],"mappings":"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"}
|
package/dist/khub.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal client for Fluid Topics' public khub REST API.
|
|
3
|
+
*
|
|
4
|
+
* {portal}/api/khub/maps → list publications (maps)
|
|
5
|
+
* {portal}/api/khub/maps/{id} → map metadata
|
|
6
|
+
* {portal}/api/khub/maps/{id}/toc → the topic tree
|
|
7
|
+
* {portal}/api/khub/maps/{id}/topics/{cid}/content→ DITA-OT HTML5 for one topic
|
|
8
|
+
*
|
|
9
|
+
* `portal` is the part before `/api/khub` and may be path-scoped
|
|
10
|
+
* (e.g. `https://www.servicenow.com/docs`, `https://docs.johnsoncontrols.com/bas`,
|
|
11
|
+
* `https://docs.fluidtopics.com`). Access is per-portal (RBAC) — gated portals
|
|
12
|
+
* return 401.
|
|
13
|
+
*/
|
|
14
|
+
export interface KhubMap {
|
|
15
|
+
id: string;
|
|
16
|
+
title: string;
|
|
17
|
+
metadata?: Array<{
|
|
18
|
+
key: string;
|
|
19
|
+
label?: string;
|
|
20
|
+
values: string[];
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
export interface KhubTocNode {
|
|
24
|
+
tocId?: string;
|
|
25
|
+
contentId?: string;
|
|
26
|
+
title: string;
|
|
27
|
+
prettyUrl?: string;
|
|
28
|
+
children?: KhubTocNode[];
|
|
29
|
+
}
|
|
30
|
+
export interface KhubClientOptions {
|
|
31
|
+
fetchImpl?: typeof fetch;
|
|
32
|
+
userAgent?: string;
|
|
33
|
+
/** Delay between requests (politeness). Default 150ms. */
|
|
34
|
+
throttleMs?: number;
|
|
35
|
+
}
|
|
36
|
+
export declare class KhubAuthError extends Error {
|
|
37
|
+
}
|
|
38
|
+
export declare class KhubClient {
|
|
39
|
+
private readonly khubBase;
|
|
40
|
+
private readonly fetchImpl;
|
|
41
|
+
private readonly userAgent;
|
|
42
|
+
private readonly throttleMs;
|
|
43
|
+
private lastAt;
|
|
44
|
+
constructor(portal: string, opts?: KhubClientOptions);
|
|
45
|
+
get base(): string;
|
|
46
|
+
listMaps(lang?: string): Promise<KhubMap[]>;
|
|
47
|
+
getMap(id: string): Promise<Record<string, unknown>>;
|
|
48
|
+
getToc(id: string): Promise<KhubTocNode[]>;
|
|
49
|
+
getTopicContent(mapId: string, contentId: string): Promise<string>;
|
|
50
|
+
private throttle;
|
|
51
|
+
private request;
|
|
52
|
+
private getJson;
|
|
53
|
+
private getText;
|
|
54
|
+
}
|
|
55
|
+
/** Accepts a portal root or a full `…/api/khub` URL; returns the khub base. */
|
|
56
|
+
export declare function normalizeKhubBase(portal: string): string;
|
|
57
|
+
/** Flatten a TOC tree to the nodes that have a contentId (i.e. real topics). */
|
|
58
|
+
export declare function tocTopics(nodes: KhubTocNode[]): KhubTocNode[];
|
|
59
|
+
//# sourceMappingURL=khub.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"khub.d.ts","sourceRoot":"","sources":["../src/khub.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CACrE;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,aAAc,SAAQ,KAAK;CAAG;AAE3C,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,MAAM,CAAK;gBAEP,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,iBAAsB;IAOxD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAEK,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAM3C,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAIpD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAK1C,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAM1D,QAAQ;YAMR,OAAO;YAYP,OAAO;YAIP,OAAO;CAGtB;AAED,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAIxD;AAED,gFAAgF;AAChF,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAU7D"}
|
package/dist/khub.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal client for Fluid Topics' public khub REST API.
|
|
3
|
+
*
|
|
4
|
+
* {portal}/api/khub/maps → list publications (maps)
|
|
5
|
+
* {portal}/api/khub/maps/{id} → map metadata
|
|
6
|
+
* {portal}/api/khub/maps/{id}/toc → the topic tree
|
|
7
|
+
* {portal}/api/khub/maps/{id}/topics/{cid}/content→ DITA-OT HTML5 for one topic
|
|
8
|
+
*
|
|
9
|
+
* `portal` is the part before `/api/khub` and may be path-scoped
|
|
10
|
+
* (e.g. `https://www.servicenow.com/docs`, `https://docs.johnsoncontrols.com/bas`,
|
|
11
|
+
* `https://docs.fluidtopics.com`). Access is per-portal (RBAC) — gated portals
|
|
12
|
+
* return 401.
|
|
13
|
+
*/
|
|
14
|
+
export class KhubAuthError extends Error {
|
|
15
|
+
}
|
|
16
|
+
export class KhubClient {
|
|
17
|
+
khubBase;
|
|
18
|
+
fetchImpl;
|
|
19
|
+
userAgent;
|
|
20
|
+
throttleMs;
|
|
21
|
+
lastAt = 0;
|
|
22
|
+
constructor(portal, opts = {}) {
|
|
23
|
+
this.khubBase = normalizeKhubBase(portal);
|
|
24
|
+
this.fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
25
|
+
this.userAgent = opts.userAgent ?? "dogsbay-format-fluidtopics-rest";
|
|
26
|
+
this.throttleMs = opts.throttleMs ?? 150;
|
|
27
|
+
}
|
|
28
|
+
get base() {
|
|
29
|
+
return this.khubBase;
|
|
30
|
+
}
|
|
31
|
+
async listMaps(lang) {
|
|
32
|
+
const q = lang ? `?lang=${encodeURIComponent(lang)}` : "";
|
|
33
|
+
const data = await this.getJson(`/maps${q}`);
|
|
34
|
+
return Array.isArray(data) ? data : [];
|
|
35
|
+
}
|
|
36
|
+
async getMap(id) {
|
|
37
|
+
return (await this.getJson(`/maps/${id}`));
|
|
38
|
+
}
|
|
39
|
+
async getToc(id) {
|
|
40
|
+
const data = await this.getJson(`/maps/${id}/toc`);
|
|
41
|
+
return Array.isArray(data) ? data : [];
|
|
42
|
+
}
|
|
43
|
+
async getTopicContent(mapId, contentId) {
|
|
44
|
+
return this.getText(`/maps/${mapId}/topics/${contentId}/content`);
|
|
45
|
+
}
|
|
46
|
+
// ── internals ───────────────────────────────────────
|
|
47
|
+
async throttle() {
|
|
48
|
+
const wait = this.throttleMs - (Date.now() - this.lastAt);
|
|
49
|
+
if (wait > 0)
|
|
50
|
+
await new Promise((r) => setTimeout(r, wait));
|
|
51
|
+
this.lastAt = Date.now();
|
|
52
|
+
}
|
|
53
|
+
async request(path) {
|
|
54
|
+
await this.throttle();
|
|
55
|
+
const res = await this.fetchImpl(`${this.khubBase}${path}`, {
|
|
56
|
+
headers: { "User-Agent": this.userAgent, Accept: "application/json, text/html" },
|
|
57
|
+
});
|
|
58
|
+
if (res.status === 401 || res.status === 403) {
|
|
59
|
+
throw new KhubAuthError(`khub ${res.status} for ${path} — portal requires authentication (gated). Use a public portal or supply credentials.`);
|
|
60
|
+
}
|
|
61
|
+
if (!res.ok)
|
|
62
|
+
throw new Error(`khub ${res.status} for ${path}`);
|
|
63
|
+
return res;
|
|
64
|
+
}
|
|
65
|
+
async getJson(path) {
|
|
66
|
+
return (await this.request(path)).json();
|
|
67
|
+
}
|
|
68
|
+
async getText(path) {
|
|
69
|
+
return (await this.request(path)).text();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Accepts a portal root or a full `…/api/khub` URL; returns the khub base. */
|
|
73
|
+
export function normalizeKhubBase(portal) {
|
|
74
|
+
let p = portal.trim().replace(/\/+$/, "");
|
|
75
|
+
if (/\/api\/khub$/.test(p))
|
|
76
|
+
return p;
|
|
77
|
+
return `${p}/api/khub`;
|
|
78
|
+
}
|
|
79
|
+
/** Flatten a TOC tree to the nodes that have a contentId (i.e. real topics). */
|
|
80
|
+
export function tocTopics(nodes) {
|
|
81
|
+
const out = [];
|
|
82
|
+
const walk = (ns) => {
|
|
83
|
+
for (const n of ns) {
|
|
84
|
+
if (n.contentId)
|
|
85
|
+
out.push(n);
|
|
86
|
+
if (n.children?.length)
|
|
87
|
+
walk(n.children);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
walk(nodes);
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=khub.js.map
|
package/dist/khub.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"khub.js","sourceRoot":"","sources":["../src/khub.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAuBH,MAAM,OAAO,aAAc,SAAQ,KAAK;CAAG;AAE3C,MAAM,OAAO,UAAU;IACJ,QAAQ,CAAS;IACjB,SAAS,CAAe;IACxB,SAAS,CAAS;IAClB,UAAU,CAAS;IAC5B,MAAM,GAAG,CAAC,CAAC;IAEnB,YAAY,MAAc,EAAE,OAA0B,EAAE;QACtD,IAAI,CAAC,QAAQ,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC,KAAK,CAAC;QACpD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,iCAAiC,CAAC;QACrE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,GAAG,CAAC;IAC3C,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,IAAa;QAC1B,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC7C,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,IAAkB,CAAC,CAAC,CAAC,EAAE,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAA4B,CAAC;IACxE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACnD,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,IAAsB,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5D,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,KAAa,EAAE,SAAiB;QACpD,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,KAAK,WAAW,SAAS,UAAU,CAAC,CAAC;IACpE,CAAC;IAED,uDAAuD;IAE/C,KAAK,CAAC,QAAQ;QACpB,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1D,IAAI,IAAI,GAAG,CAAC;YAAE,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;QAC5D,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC3B,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,IAAY;QAChC,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACtB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,QAAQ,GAAG,IAAI,EAAE,EAAE;YAC1D,OAAO,EAAE,EAAE,YAAY,EAAE,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,6BAA6B,EAAE;SACjF,CAAC,CAAC;QACH,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC7C,MAAM,IAAI,aAAa,CAAC,QAAQ,GAAG,CAAC,MAAM,QAAQ,IAAI,uFAAuF,CAAC,CAAC;QACjJ,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,MAAM,QAAQ,IAAI,EAAE,CAAC,CAAC;QAC/D,OAAO,GAAG,CAAC;IACb,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,IAAY;QAChC,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3C,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,IAAY;QAChC,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3C,CAAC;CACF;AAED,+EAA+E;AAC/E,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC1C,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IACrC,OAAO,GAAG,CAAC,WAAW,CAAC;AACzB,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,SAAS,CAAC,KAAoB;IAC5C,MAAM,GAAG,GAAkB,EAAE,CAAC;IAC9B,MAAM,IAAI,GAAG,CAAC,EAAiB,EAAE,EAAE;QACjC,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;YACnB,IAAI,CAAC,CAAC,SAAS;gBAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC7B,IAAI,CAAC,CAAC,QAAQ,EAAE,MAAM;gBAAE,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC,CAAC;IACF,IAAI,CAAC,KAAK,CAAC,CAAC;IACZ,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/links.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** `/r/customer-relationship-management/crm-about-csm-in-crm.html` → `customer-relationship-management/crm-about-csm-in-crm` */
|
|
2
|
+
export declare function slugFromPrettyUrl(prettyUrl: string | undefined): string;
|
|
3
|
+
export declare function routeFromSlug(slug: string, hrefPrefix: string): string;
|
|
4
|
+
/**
|
|
5
|
+
* Resolve an in-content href to a Dogsbay route. Relative `.html` and reader
|
|
6
|
+
* (`/r/…`) links resolve into the route space; external/anchor/scheme links
|
|
7
|
+
* pass through. Returns `{ href, resolved }`.
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveHref(href: string, fromSlug: string, hrefPrefix?: string): {
|
|
10
|
+
href: string;
|
|
11
|
+
resolved: boolean;
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=links.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"links.d.ts","sourceRoot":"","sources":["../src/links.ts"],"names":[],"mappings":"AAUA,gIAAgI;AAChI,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAKvE;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAGtE;AAOD;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,SAAU,GACnB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAqBrC"}
|
package/dist/links.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slug + link resolution for FT khub content.
|
|
3
|
+
*
|
|
4
|
+
* khub topics carry a `prettyUrl` like `/r/customer-relationship-management/
|
|
5
|
+
* crm-about-csm-in-crm.html`; we derive a clean route slug from it. In-content
|
|
6
|
+
* links are DITA-OT `.html` hrefs (relative or reader-absolute) resolved to the
|
|
7
|
+
* same route space.
|
|
8
|
+
*/
|
|
9
|
+
import { posix } from "node:path";
|
|
10
|
+
/** `/r/customer-relationship-management/crm-about-csm-in-crm.html` → `customer-relationship-management/crm-about-csm-in-crm` */
|
|
11
|
+
export function slugFromPrettyUrl(prettyUrl) {
|
|
12
|
+
if (!prettyUrl)
|
|
13
|
+
return "";
|
|
14
|
+
let s = prettyUrl.split("#")[0].split("?")[0].trim();
|
|
15
|
+
s = s.replace(/^\/+/, "").replace(/^r\//, "").replace(/\.html?$/i, "");
|
|
16
|
+
return s.replace(/\/+$/, "").toLowerCase();
|
|
17
|
+
}
|
|
18
|
+
export function routeFromSlug(slug, hrefPrefix) {
|
|
19
|
+
if (!slug)
|
|
20
|
+
return hrefPrefix || "/";
|
|
21
|
+
return `${hrefPrefix}/${slug}`;
|
|
22
|
+
}
|
|
23
|
+
function splitAnchor(s) {
|
|
24
|
+
const i = s.indexOf("#");
|
|
25
|
+
return i >= 0 ? [s.slice(0, i), s.slice(i + 1)] : [s, ""];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Resolve an in-content href to a Dogsbay route. Relative `.html` and reader
|
|
29
|
+
* (`/r/…`) links resolve into the route space; external/anchor/scheme links
|
|
30
|
+
* pass through. Returns `{ href, resolved }`.
|
|
31
|
+
*/
|
|
32
|
+
export function resolveHref(href, fromSlug, hrefPrefix = "/docs") {
|
|
33
|
+
if (!href)
|
|
34
|
+
return { href, resolved: false };
|
|
35
|
+
if (href.startsWith("#"))
|
|
36
|
+
return { href, resolved: true };
|
|
37
|
+
const [path, anchor] = splitAnchor(href);
|
|
38
|
+
const withAnchor = (route) => route + (anchor ? `#${anchor}` : "");
|
|
39
|
+
// Reader-absolute (/r/…) → slug
|
|
40
|
+
if (path.startsWith("/r/")) {
|
|
41
|
+
return { href: withAnchor(routeFromSlug(slugFromPrettyUrl(path), hrefPrefix)), resolved: true };
|
|
42
|
+
}
|
|
43
|
+
// External / scheme (http, mailto, tel)
|
|
44
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(href))
|
|
45
|
+
return { href, resolved: true };
|
|
46
|
+
// Relative .html → resolve against the current topic's dir
|
|
47
|
+
if (/\.html?$/i.test(path) || path === "") {
|
|
48
|
+
const dir = posix.dirname(fromSlug || "index");
|
|
49
|
+
const rel = posix.normalize(posix.join(dir, path || ".")).replace(/\.html?$/i, "");
|
|
50
|
+
const slug = rel.replace(/^\.\/?/, "").replace(/^\/+/, "").toLowerCase();
|
|
51
|
+
return { href: withAnchor(routeFromSlug(slug, hrefPrefix)), resolved: true };
|
|
52
|
+
}
|
|
53
|
+
return { href, resolved: false };
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"links.js","sourceRoot":"","sources":["../src/links.ts"],"names":[],"mappings":"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"}
|
package/dist/nav.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a Dogsbay nav subtree from a khub map TOC (the authoritative DITA map).
|
|
3
|
+
*/
|
|
4
|
+
import type { NavItem } from "@dogsbay/types";
|
|
5
|
+
import type { KhubTocNode } from "./khub.js";
|
|
6
|
+
export declare function tocToNav(nodes: KhubTocNode[], hrefPrefix?: string): NavItem[];
|
|
7
|
+
//# sourceMappingURL=nav.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nav.d.ts","sourceRoot":"","sources":["../src/nav.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAG7C,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,UAAU,SAAU,GAAG,OAAO,EAAE,CAU9E"}
|
package/dist/nav.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { slugFromPrettyUrl, routeFromSlug } from "./links.js";
|
|
2
|
+
export function tocToNav(nodes, hrefPrefix = "/docs") {
|
|
3
|
+
const out = [];
|
|
4
|
+
for (const n of nodes) {
|
|
5
|
+
const item = { label: n.title };
|
|
6
|
+
const slug = slugFromPrettyUrl(n.prettyUrl);
|
|
7
|
+
if (slug)
|
|
8
|
+
item.href = routeFromSlug(slug, hrefPrefix);
|
|
9
|
+
if (n.children?.length)
|
|
10
|
+
item.children = tocToNav(n.children, hrefPrefix);
|
|
11
|
+
out.push(item);
|
|
12
|
+
}
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=nav.js.map
|
package/dist/nav.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nav.js","sourceRoot":"","sources":["../src/nav.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE9D,MAAM,UAAU,QAAQ,CAAC,KAAoB,EAAE,UAAU,GAAG,OAAO;IACjE,MAAM,GAAG,GAAc,EAAE,CAAC;IAC1B,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,IAAI,GAAY,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAC5C,IAAI,IAAI;YAAE,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QACtD,IAAI,CAAC,CAAC,QAAQ,EAAE,MAAM;YAAE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACzE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single-page (whole-map) assembly.
|
|
3
|
+
*
|
|
4
|
+
* FT publishes some books as one continuous page (and many readers *prefer* a
|
|
5
|
+
* single page over "bitty" per-topic pages — the fragmentation debate). This
|
|
6
|
+
* mode rolls an entire khub map into ONE Dogsbay page: each TOC node becomes a
|
|
7
|
+
* heading (level = TOC depth), the topic body nests beneath it, and internal
|
|
8
|
+
* xrefs resolve to in-page anchors instead of separate routes.
|
|
9
|
+
*
|
|
10
|
+
* Heading model: the page title (the book) is the implicit H1, so a top-level
|
|
11
|
+
* TOC node is H2, its children H3, … (clamped at H6). A topic's own body
|
|
12
|
+
* headings (DITA section titles, normally H2) are shifted down by `level - 1`
|
|
13
|
+
* so they sit one rank below their topic's title.
|
|
14
|
+
*/
|
|
15
|
+
import type { TreeNode, NavItem } from "@dogsbay/types";
|
|
16
|
+
import type { KhubTocNode } from "./khub.js";
|
|
17
|
+
export interface BookAnchor {
|
|
18
|
+
node: KhubTocNode;
|
|
19
|
+
depth: number;
|
|
20
|
+
/** Heading level for this node's title (clamped 2..6). */
|
|
21
|
+
level: number;
|
|
22
|
+
/** Stable, page-unique anchor id. */
|
|
23
|
+
anchor: string;
|
|
24
|
+
}
|
|
25
|
+
export interface BookAnchors {
|
|
26
|
+
ordered: BookAnchor[];
|
|
27
|
+
/** tocId → anchor (for xref resolution). */
|
|
28
|
+
byTocId: Map<string, string>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Walk a map TOC in document order, assigning every node a heading level and a
|
|
32
|
+
* page-unique anchor (derived from its prettyUrl basename, else its title).
|
|
33
|
+
*/
|
|
34
|
+
export declare function buildBookAnchors(toc: KhubTocNode[]): BookAnchors;
|
|
35
|
+
/** A heading node for a TOC entry's title, carrying the explicit anchor id. */
|
|
36
|
+
export declare function bookHeading(level: number, title: string, anchor: string): TreeNode;
|
|
37
|
+
/** Shift every heading in a (topic body) tree down by `offset` ranks (max H6). */
|
|
38
|
+
export declare function shiftHeadings(tree: TreeNode[], offset: number): void;
|
|
39
|
+
/**
|
|
40
|
+
* Longest common path prefix (slash-segment-wise) of the topic slugs — used as
|
|
41
|
+
* the book's route (e.g. the FT book base `…/metasys-ui…/16.0`). Falls back to
|
|
42
|
+
* `fallback` (slugified title) when there's no shared prefix.
|
|
43
|
+
*/
|
|
44
|
+
export declare function bookSlugFrom(slugs: string[], fallback: string): string;
|
|
45
|
+
/** One nav leaf per book → its single page (anchors aren't carried in nav.yml). */
|
|
46
|
+
export declare function bookNavItem(title: string, route: string): NavItem;
|
|
47
|
+
//# sourceMappingURL=single-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"single-page.d.ts","sourceRoot":"","sources":["../src/single-page.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAI7C,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,4CAA4C;IAC5C,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,WAAW,CAuBhE;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,QAAQ,CAMlF;AAED,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAUpE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAWtE;AAED,mFAAmF;AACnF,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAEjE"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { slugFromPrettyUrl } from "./links.js";
|
|
2
|
+
import { slugify } from "./headings.js";
|
|
3
|
+
/**
|
|
4
|
+
* Walk a map TOC in document order, assigning every node a heading level and a
|
|
5
|
+
* page-unique anchor (derived from its prettyUrl basename, else its title).
|
|
6
|
+
*/
|
|
7
|
+
export function buildBookAnchors(toc) {
|
|
8
|
+
const ordered = [];
|
|
9
|
+
const byTocId = new Map();
|
|
10
|
+
const seen = new Map();
|
|
11
|
+
const uniq = (base) => {
|
|
12
|
+
const b = base || "section";
|
|
13
|
+
const n = seen.get(b) ?? 0;
|
|
14
|
+
seen.set(b, n + 1);
|
|
15
|
+
return n === 0 ? b : `${b}-${n}`;
|
|
16
|
+
};
|
|
17
|
+
const walk = (nodes, depth) => {
|
|
18
|
+
for (const n of nodes) {
|
|
19
|
+
const seg = n.prettyUrl ? slugFromPrettyUrl(n.prettyUrl).split("/").pop() ?? "" : "";
|
|
20
|
+
const anchor = uniq(slugify(seg || n.title || "section"));
|
|
21
|
+
ordered.push({ node: n, depth, level: Math.min(depth + 2, 6), anchor });
|
|
22
|
+
if (n.tocId)
|
|
23
|
+
byTocId.set(n.tocId, anchor);
|
|
24
|
+
if (n.children?.length)
|
|
25
|
+
walk(n.children, depth + 1);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
walk(toc, 0);
|
|
29
|
+
return { ordered, byTocId };
|
|
30
|
+
}
|
|
31
|
+
/** A heading node for a TOC entry's title, carrying the explicit anchor id. */
|
|
32
|
+
export function bookHeading(level, title, anchor) {
|
|
33
|
+
return {
|
|
34
|
+
type: "heading",
|
|
35
|
+
props: { level, slug: anchor, text: title },
|
|
36
|
+
children: [{ type: "prose", inline: [{ type: "text", text: title }] }],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** Shift every heading in a (topic body) tree down by `offset` ranks (max H6). */
|
|
40
|
+
export function shiftHeadings(tree, offset) {
|
|
41
|
+
if (offset <= 0)
|
|
42
|
+
return;
|
|
43
|
+
for (const n of tree) {
|
|
44
|
+
if (n.type === "heading") {
|
|
45
|
+
const lvl = n.props?.level ?? 1;
|
|
46
|
+
if (!n.props)
|
|
47
|
+
n.props = {};
|
|
48
|
+
n.props.level = Math.min(lvl + offset, 6);
|
|
49
|
+
}
|
|
50
|
+
if (n.children)
|
|
51
|
+
shiftHeadings(n.children, offset);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Longest common path prefix (slash-segment-wise) of the topic slugs — used as
|
|
56
|
+
* the book's route (e.g. the FT book base `…/metasys-ui…/16.0`). Falls back to
|
|
57
|
+
* `fallback` (slugified title) when there's no shared prefix.
|
|
58
|
+
*/
|
|
59
|
+
export function bookSlugFrom(slugs, fallback) {
|
|
60
|
+
const parts = slugs.filter(Boolean).map((s) => s.split("/"));
|
|
61
|
+
if (!parts.length)
|
|
62
|
+
return slugify(fallback);
|
|
63
|
+
const first = parts[0];
|
|
64
|
+
let i = 0;
|
|
65
|
+
for (; i < first.length; i++) {
|
|
66
|
+
const seg = first[i];
|
|
67
|
+
if (!parts.every((p) => p[i] === seg))
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
const prefix = first.slice(0, i).join("/");
|
|
71
|
+
return prefix || slugify(fallback);
|
|
72
|
+
}
|
|
73
|
+
/** One nav leaf per book → its single page (anchors aren't carried in nav.yml). */
|
|
74
|
+
export function bookNavItem(title, route) {
|
|
75
|
+
return { label: title, href: route };
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=single-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"single-page.js","sourceRoot":"","sources":["../src/single-page.ts"],"names":[],"mappings":"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"}
|
package/dist/tree.d.ts
ADDED