@dogsbay/format-fluidtopics-rest 0.2.0-beta.101
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 +36 -0
- package/dist/frontmatter.d.ts.map +1 -0
- package/dist/frontmatter.js +78 -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 +17 -0
- package/dist/html-to-tree.d.ts.map +1 -0
- package/dist/html-to-tree.js +309 -0
- package/dist/html-to-tree.js.map +1 -0
- package/dist/importer.d.ts +87 -0
- package/dist/importer.d.ts.map +1 -0
- package/dist/importer.js +354 -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 +86 -0
- package/dist/khub.d.ts.map +1 -0
- package/dist/khub.js +111 -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 +70 -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
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# @dogsbay/format-fluidtopics-rest
|
|
2
|
+
|
|
3
|
+
Importer for a **live Fluid Topics portal** via the public **khub REST API**
|
|
4
|
+
(Option B of the FT→Dogsbay migration). It reverse-engineers each topic's
|
|
5
|
+
**DITA-OT HTML5** — where the DITA element vocabulary survives in `class` names
|
|
6
|
+
(`conbody`/`taskbody`/`refbody`, `shortdesc`, `section`, CALS tables, `span.ph`,
|
|
7
|
+
…) — into Dogsbay's universal `TreeNode` model, producing `ExportPage[]` +
|
|
8
|
+
`NavItem[]`.
|
|
9
|
+
|
|
10
|
+
Works on any **public** FT portal. Gated portals (RBAC) throw `KhubAuthError`.
|
|
11
|
+
|
|
12
|
+
The sibling [`@dogsbay/format-fluidtopics-markdown`](../format-fluidtopics-markdown)
|
|
13
|
+
does the same job from a Git Markdown export (Option A).
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { importFluidTopicsRest } from "@dogsbay/format-fluidtopics-rest";
|
|
19
|
+
|
|
20
|
+
const { pages, nav } = await importFluidTopicsRest({
|
|
21
|
+
portalBase: "https://www.servicenow.com/docs", // or .../bas, docs.fluidtopics.com
|
|
22
|
+
titleFilter: "Customer Relationship Management", // or maps: ["<mapId>"]
|
|
23
|
+
hrefPrefix: "/docs",
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Or the low-level converter / client:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { htmlToTree, KhubClient } from "@dogsbay/format-fluidtopics-rest";
|
|
31
|
+
const { tree, title, topicType } = htmlToTree(htmlString); // topicType: concept|task|reference|topic
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
CLI: `dogsbay migrate-fluidtopics-rest <portal-url> [--map <id>] [--title-filter <s>] [--single-page] [-o <dir>]`.
|
|
35
|
+
|
|
36
|
+
### Single-page (whole-book) mode
|
|
37
|
+
|
|
38
|
+
Pass `--single-page` / `singlePage: true` to roll each map into **one page** (the
|
|
39
|
+
whole book) instead of one page per topic — for readers who prefer a single long
|
|
40
|
+
page over "bitty" topics, and to match FT portals that serve a book as one page.
|
|
41
|
+
TOC nodes become nested headings (level = TOC depth), internal xrefs become
|
|
42
|
+
in-page anchors, and the page lands at the book's common path prefix
|
|
43
|
+
(e.g. `…/metasys-ui-and-jct-technical-bulletin/16.0`).
|
|
44
|
+
|
|
45
|
+
## What it does
|
|
46
|
+
|
|
47
|
+
- **khub harvest** — `maps` → `maps/{id}/toc` → `maps/{id}/topics/{cid}/content`; path-scoped bases (`…/bas/api/khub`); polite throttling; 401/403 → `KhubAuthError`.
|
|
48
|
+
- **HTML5 → TreeNode** — topic type from the body class; DITA sections flattened to headings; CALS tables → structured `table` nodes; `span.ph`/`keyword`/`uicontrol` unwrapped, `codeph` → inline code; FT chrome (`zDocs*`, `ai-summary`, `sentence`, `css-…`, `toggle/summary/tooltip`) stripped.
|
|
49
|
+
- **Frontmatter** — `topic_type` → `type`, plus `lang`/`version` from the map metadata.
|
|
50
|
+
- **Nav** — from each map's TOC (the authoritative DITA map), with route slugs derived from `prettyUrl`.
|
|
51
|
+
|
|
52
|
+
## Rendering fidelity
|
|
53
|
+
|
|
54
|
+
Tuned against the live FT portals; each item is regression-tested. Full story in
|
|
55
|
+
[`docs-dev/fluidtopics-rest-importer.md`](../../docs-dev/fluidtopics-rest-importer.md).
|
|
56
|
+
|
|
57
|
+
- **Internal xrefs** — FT renders them as hrefless `<span data-tocid data-mapid>`
|
|
58
|
+
(resolved by its client at runtime, zero `href`s in the static HTML). The
|
|
59
|
+
importer builds a `tocId → slug` index from the selected maps' TOCs and resolves
|
|
60
|
+
them to real routes, falling back to plain text (not a dead link) when out of set.
|
|
61
|
+
- **Figures / inline images** — block `<img>` survive; base64 `data:` images are
|
|
62
|
+
extracted to real `content/_assets/…` files (filename from FT metadata).
|
|
63
|
+
- **Lists & inline runs** — consecutive inline content is coalesced into one
|
|
64
|
+
paragraph, so xrefs inside list items resolve and prose doesn't fragment.
|
|
65
|
+
- **Tables** — CALS → structured tables; cells with block content (lists,
|
|
66
|
+
paragraphs) are preserved (HTML tables per the Dogsbay-MD convention).
|
|
67
|
+
- **Definition lists** — `<dl>/<dt>/<dd>` → a real deflist (`dt.inline` term + `dd`
|
|
68
|
+
block children), not literal `: ` text.
|
|
69
|
+
|
|
70
|
+
## Verified (live, 2026-06)
|
|
71
|
+
|
|
72
|
+
ServiceNow `www.servicenow.com/docs` (CRM, 24 topics), Johnson Controls
|
|
73
|
+
`docs.johnsoncontrols.com/bas` (Metasys bulletin, 55), Fluid Topics
|
|
74
|
+
`docs.fluidtopics.com` (13). Notably, REST succeeds on ServiceNow's CRM
|
|
75
|
+
publication where the **Markdown export (Option A) failed** (its `index.md` TOC
|
|
76
|
+
was stale: 1/24 targets existed) — the live khub TOC+content is authoritative.
|
|
77
|
+
|
|
78
|
+
## Known gaps
|
|
79
|
+
|
|
80
|
+
- Nav-card landing grids come through as a generic table (Option A special-cases
|
|
81
|
+
them into `:::cards`) — a `nav-card`-detection pass is future work.
|
|
82
|
+
- **Cross-map** xrefs resolve only when the target map is in the import set
|
|
83
|
+
(single-`--map` runs leave them as text).
|
|
84
|
+
- External image **URLs** are kept as-is (not downloaded); only inline `data:`
|
|
85
|
+
images are localized to `_assets`.
|
|
86
|
+
- conref/keyref/profiling were already resolved/flattened upstream by FT's
|
|
87
|
+
DITA-OT run (not recoverable, and not needed for delivery).
|
package/dist/assets.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ParsedDataUri {
|
|
2
|
+
mime: string;
|
|
3
|
+
data: Buffer;
|
|
4
|
+
}
|
|
5
|
+
/** Parse a `data:` URI into its mime + decoded bytes. Returns null if not one. */
|
|
6
|
+
export declare function parseDataUri(src: string): ParsedDataUri | null;
|
|
7
|
+
export declare function sanitizeFilename(name: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Compute the content-relative asset path (`_assets/<dir>/<file>`) for a
|
|
10
|
+
* decoded image, using the FT display-name as the filename.
|
|
11
|
+
*/
|
|
12
|
+
export declare function assetPath(resourceId: string | undefined, displayName: string | undefined, parsed: ParsedDataUri): string;
|
|
13
|
+
//# sourceMappingURL=assets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../src/assets.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAQ9D;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQrD;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,MAAM,EAAE,aAAa,GACpB,MAAM,CAOR"}
|
package/dist/assets.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn inline `data:` images (FT inlines figure PNGs / SVG icons as base64) into
|
|
3
|
+
* real content-rooted asset files, per the Dogsbay `_assets` convention:
|
|
4
|
+
* bytes written under `content/_assets/<rel>/<file>` and referenced as
|
|
5
|
+
* `/_assets/<rel>/<file>` (format-astro's copyAssets propagates them to
|
|
6
|
+
* astro/public on build).
|
|
7
|
+
*
|
|
8
|
+
* The human filename comes from FT metadata (`data-ft-asset-display-name`); the
|
|
9
|
+
* directory is an 8-char hash of the FT `data-ft-resource-id` (stable + dedups
|
|
10
|
+
* the same resource reused across topics) or, lacking that, of the bytes.
|
|
11
|
+
*/
|
|
12
|
+
import { createHash } from "node:crypto";
|
|
13
|
+
const EXT_BY_MIME = {
|
|
14
|
+
"image/png": "png",
|
|
15
|
+
"image/jpeg": "jpg",
|
|
16
|
+
"image/jpg": "jpg",
|
|
17
|
+
"image/gif": "gif",
|
|
18
|
+
"image/svg+xml": "svg",
|
|
19
|
+
"image/webp": "webp",
|
|
20
|
+
"image/avif": "avif",
|
|
21
|
+
};
|
|
22
|
+
/** Parse a `data:` URI into its mime + decoded bytes. Returns null if not one. */
|
|
23
|
+
export function parseDataUri(src) {
|
|
24
|
+
const m = /^data:([^;,]+)(;base64)?,([\s\S]*)$/.exec(src);
|
|
25
|
+
if (!m)
|
|
26
|
+
return null;
|
|
27
|
+
const mime = m[1];
|
|
28
|
+
const isBase64 = !!m[2];
|
|
29
|
+
const payload = m[3];
|
|
30
|
+
const data = isBase64 ? Buffer.from(payload, "base64") : Buffer.from(decodeURIComponent(payload), "utf-8");
|
|
31
|
+
return { mime, data };
|
|
32
|
+
}
|
|
33
|
+
export function sanitizeFilename(name) {
|
|
34
|
+
return (name
|
|
35
|
+
.trim()
|
|
36
|
+
.replace(/[^\w.\-]+/g, "-")
|
|
37
|
+
.replace(/-+/g, "-")
|
|
38
|
+
.replace(/^[-.]+|[-.]+$/g, "") || "image");
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Compute the content-relative asset path (`_assets/<dir>/<file>`) for a
|
|
42
|
+
* decoded image, using the FT display-name as the filename.
|
|
43
|
+
*/
|
|
44
|
+
export function assetPath(resourceId, displayName, parsed) {
|
|
45
|
+
const ext = EXT_BY_MIME[parsed.mime] ?? "bin";
|
|
46
|
+
let file = displayName ? sanitizeFilename(displayName) : "";
|
|
47
|
+
if (!file || !/\.[a-z0-9]+$/i.test(file))
|
|
48
|
+
file = `${file || "image"}.${ext}`;
|
|
49
|
+
const key = resourceId || createHash("sha1").update(parsed.data).digest("hex");
|
|
50
|
+
const dir = createHash("sha1").update(key).digest("hex").slice(0, 8);
|
|
51
|
+
return `_assets/${dir}/${file}`;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=assets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../src/assets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,WAAW,GAA2B;IAC1C,WAAW,EAAE,KAAK;IAClB,YAAY,EAAE,KAAK;IACnB,WAAW,EAAE,KAAK;IAClB,WAAW,EAAE,KAAK;IAClB,eAAe,EAAE,KAAK;IACtB,YAAY,EAAE,MAAM;IACpB,YAAY,EAAE,MAAM;CACrB,CAAC;AAOF,kFAAkF;AAClF,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,MAAM,CAAC,GAAG,qCAAqC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1D,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAClB,MAAM,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxB,MAAM,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACrB,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;IAC3G,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,CACL,IAAI;SACD,IAAI,EAAE;SACN,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC;SAC1B,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,IAAI,OAAO,CAC5C,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CACvB,UAA8B,EAC9B,WAA+B,EAC/B,MAAqB;IAErB,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC;IAC9C,IAAI,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5D,IAAI,CAAC,IAAI,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,IAAI,GAAG,GAAG,IAAI,IAAI,OAAO,IAAI,GAAG,EAAE,CAAC;IAC7E,MAAM,GAAG,GAAG,UAAU,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/E,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACrE,OAAO,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC;AAClC,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FormatPlugin } from "@dogsbay/types";
|
|
2
|
+
/**
|
|
3
|
+
* Fluid Topics REST format plugin.
|
|
4
|
+
*
|
|
5
|
+
* Import-only; the "source" is a **portal URL** (not a local path), so it's
|
|
6
|
+
* driven by the `migrate-fluidtopics-rest` command rather than path auto-detect.
|
|
7
|
+
*/
|
|
8
|
+
export declare const plugin: FormatPlugin;
|
|
9
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAuB,MAAM,gBAAgB,CAAC;AAGxE;;;;;GAKG;AACH,eAAO,MAAM,MAAM,EAAE,YAoBpB,CAAC"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { importFluidTopicsRest } from "./importer.js";
|
|
2
|
+
/**
|
|
3
|
+
* Fluid Topics REST format plugin.
|
|
4
|
+
*
|
|
5
|
+
* Import-only; the "source" is a **portal URL** (not a local path), so it's
|
|
6
|
+
* driven by the `migrate-fluidtopics-rest` command rather than path auto-detect.
|
|
7
|
+
*/
|
|
8
|
+
export const plugin = {
|
|
9
|
+
name: "fluidtopics-rest",
|
|
10
|
+
canImport: true,
|
|
11
|
+
canExport: false,
|
|
12
|
+
detectSource: () => false, // URL source — invoked explicitly
|
|
13
|
+
importOptions: [],
|
|
14
|
+
async import(source, opts) {
|
|
15
|
+
const { pages, nav } = await importFluidTopicsRest({
|
|
16
|
+
portalBase: source,
|
|
17
|
+
maps: Array.isArray(opts.maps) ? opts.maps : undefined,
|
|
18
|
+
titleFilter: typeof opts.titleFilter === "string" ? opts.titleFilter : undefined,
|
|
19
|
+
lang: typeof opts.lang === "string" ? opts.lang : undefined,
|
|
20
|
+
hrefPrefix: typeof opts.hrefPrefix === "string" ? opts.hrefPrefix : undefined,
|
|
21
|
+
throttleMs: typeof opts.throttleMs === "number" ? opts.throttleMs : undefined,
|
|
22
|
+
taxonomyNames: Array.isArray(opts.taxonomyNames) ? opts.taxonomyNames : undefined,
|
|
23
|
+
});
|
|
24
|
+
opts.siteName = opts.siteName || "Documentation";
|
|
25
|
+
return { pages, nav };
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,MAAM,GAAiB;IAClC,IAAI,EAAE,kBAAkB;IACxB,SAAS,EAAE,IAAI;IACf,SAAS,EAAE,KAAK;IAChB,YAAY,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,kCAAkC;IAC7D,aAAa,EAAE,EAAE;IAEjB,KAAK,CAAC,MAAM,CAAC,MAAc,EAAE,IAA6B;QACxD,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,MAAM,qBAAqB,CAAC;YACjD,UAAU,EAAE,MAAM;YAClB,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,IAAI,CAAC,IAAiB,CAAC,CAAC,CAAC,SAAS;YACpE,WAAW,EAAE,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;YAChF,IAAI,EAAE,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;YAC3D,UAAU,EAAE,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;YAC7E,UAAU,EAAE,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;YAC7E,aAAa,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAE,IAAI,CAAC,aAA0B,CAAC,CAAC,CAAC,SAAS;SAChG,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,eAAe,CAAC;QACjD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IACxB,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build Dogsbay frontmatter for a topic from the DITA-OT-derived topic type
|
|
3
|
+
* plus the map's khub metadata (locale, release/family).
|
|
4
|
+
*/
|
|
5
|
+
import type { KhubMap } from "./khub.js";
|
|
6
|
+
type Meta = NonNullable<KhubMap["metadata"]>;
|
|
7
|
+
export interface BuildFrontmatterArgs {
|
|
8
|
+
title: string;
|
|
9
|
+
/** concept | task | reference | glossentry | topic */
|
|
10
|
+
topicType: string;
|
|
11
|
+
mapMeta?: Meta;
|
|
12
|
+
/**
|
|
13
|
+
* Per-topic metadata (from `/maps/{id}/topics`). When present its values take
|
|
14
|
+
* precedence over `mapMeta` — topic-level facets are more specific than the
|
|
15
|
+
* map's. The DITA subjectScheme classification arrives here as the `PBS`
|
|
16
|
+
* ("Subject matter") facet.
|
|
17
|
+
*/
|
|
18
|
+
topicMeta?: Meta;
|
|
19
|
+
/**
|
|
20
|
+
* Extra FT metadata keys to copy into frontmatter, kept **verbatim** (the
|
|
21
|
+
* `ft:` prefix is preserved to namespace them). Allowlist, not blocklist: the
|
|
22
|
+
* internal `ft:` keys never land unless explicitly named here.
|
|
23
|
+
*/
|
|
24
|
+
metadataAllow?: readonly string[];
|
|
25
|
+
/**
|
|
26
|
+
* Emit the `ft:`-namespaced technical numerics (wordCount/contentSize/…).
|
|
27
|
+
* True for book/single-page pages (the map totals are correct there) and for
|
|
28
|
+
* per-topic pages only when full per-topic metadata was fetched. Off on the
|
|
29
|
+
* cheap per-topic path, where the only available values are misleading book
|
|
30
|
+
* totals.
|
|
31
|
+
*/
|
|
32
|
+
technical?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export declare function buildFrontmatter(args: BuildFrontmatterArgs): Record<string, unknown>;
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=frontmatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../src/frontmatter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,KAAK,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;AAmB7C,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,IAAI,CAAC;IACf;;;;;OAKG;IACH,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB;;;;OAIG;IACH,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkDpF"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FT technical metadata kept verbatim under the `ft:` namespace (as numbers) so
|
|
3
|
+
* they sit apart from the semantic facets. `wordCount`/`outputSize` are
|
|
4
|
+
* per-topic; `contentSize`/`attachmentsSize` are map-level (so they repeat
|
|
5
|
+
* across a book's topics).
|
|
6
|
+
*/
|
|
7
|
+
const TECHNICAL_KEYS = ["ft:wordCount", "ft:contentSize", "ft:outputSize", "ft:attachmentsSize"];
|
|
8
|
+
function metaValue(meta, ...keys) {
|
|
9
|
+
if (!meta)
|
|
10
|
+
return undefined;
|
|
11
|
+
for (const key of keys) {
|
|
12
|
+
const hit = meta.find((m) => m.key === key);
|
|
13
|
+
if (hit?.values?.length)
|
|
14
|
+
return hit.values[0];
|
|
15
|
+
}
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
export function buildFrontmatter(args) {
|
|
19
|
+
const fm = { title: args.title };
|
|
20
|
+
// Prefer the per-topic value, fall back to the map's.
|
|
21
|
+
const mv = (...keys) => metaValue(args.topicMeta, ...keys) ?? metaValue(args.mapMeta, ...keys);
|
|
22
|
+
// topic_type → type (skip the generic "topic")
|
|
23
|
+
if (args.topicType && args.topicType !== "topic")
|
|
24
|
+
fm.type = args.topicType;
|
|
25
|
+
const locale = mv("ft:locale", "locale");
|
|
26
|
+
if (locale)
|
|
27
|
+
fm.lang = locale;
|
|
28
|
+
const version = mv("family", "version", "ft:version", "Version_FT", "FT_Version");
|
|
29
|
+
if (version)
|
|
30
|
+
fm.version = version;
|
|
31
|
+
// Faceted metadata → search filters (Pagefind auto-discovers the
|
|
32
|
+
// `data-pagefind-filter` divs DocsLayout emits from these fields).
|
|
33
|
+
// `category` / `audience` are built-in facets (parseMeta reads them
|
|
34
|
+
// directly); `editorialType` / `version` / `subject` are custom taxonomies
|
|
35
|
+
// the importer's `taxonomyNames` picks up. See docs-dev/fluidtopics-rest-importer.md.
|
|
36
|
+
const category = mv("Category");
|
|
37
|
+
if (category)
|
|
38
|
+
fm.category = category;
|
|
39
|
+
const audience = mv("audience");
|
|
40
|
+
if (audience)
|
|
41
|
+
fm.audience = audience;
|
|
42
|
+
const editorialType = mv("ft:editorialType");
|
|
43
|
+
if (editorialType)
|
|
44
|
+
fm.editorialType = editorialType;
|
|
45
|
+
// DITA subjectScheme classification → "Subject matter" facet.
|
|
46
|
+
const subject = mv("PBS");
|
|
47
|
+
if (subject)
|
|
48
|
+
fm.subject = subject;
|
|
49
|
+
const product = mv("Product");
|
|
50
|
+
if (product)
|
|
51
|
+
fm.product = product;
|
|
52
|
+
const author = mv("author");
|
|
53
|
+
if (author)
|
|
54
|
+
fm.author = author;
|
|
55
|
+
const source = mv("ft:sourceName");
|
|
56
|
+
if (source)
|
|
57
|
+
fm.source = source;
|
|
58
|
+
// Last content change → DocsFooter "Last updated". Per-topic only: it lives
|
|
59
|
+
// solely in the full per-topic metadata, so it's absent on the cheap path
|
|
60
|
+
// (we deliberately don't fall back to the book-level ft:lastEdition).
|
|
61
|
+
const updated = metaValue(args.topicMeta, "ft:lastTechChange");
|
|
62
|
+
if (updated)
|
|
63
|
+
fm.updated = updated;
|
|
64
|
+
// Technical fields (only where the values are meaningful — see `technical`),
|
|
65
|
+
// plus any --metadata extras (always): kept under the `ft:` namespace,
|
|
66
|
+
// verbatim, coerced to number when numeric.
|
|
67
|
+
const keys = [...(args.technical ? TECHNICAL_KEYS : []), ...(args.metadataAllow ?? [])];
|
|
68
|
+
for (const key of keys) {
|
|
69
|
+
if (key in fm)
|
|
70
|
+
continue;
|
|
71
|
+
const v = mv(key);
|
|
72
|
+
if (v === undefined)
|
|
73
|
+
continue;
|
|
74
|
+
fm[key] = /^\d+$/.test(v) ? Number(v) : v;
|
|
75
|
+
}
|
|
76
|
+
return fm;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=frontmatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../src/frontmatter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heading extraction + slug population.
|
|
3
|
+
*
|
|
4
|
+
* Walks the tree, computes a stable slug for each heading (de-duplicated),
|
|
5
|
+
* writes `slug`/`text` back onto the heading node's props (so the renderer
|
|
6
|
+
* can emit anchors), and returns the flat Heading[] for TOC generation.
|
|
7
|
+
*/
|
|
8
|
+
import type { TreeNode, Heading } from "@dogsbay/types";
|
|
9
|
+
export declare function extractHeadings(tree: TreeNode[]): Heading[];
|
|
10
|
+
/** Extract plain text from a node, preferring structured inline content. */
|
|
11
|
+
export declare function extractText(node: TreeNode): string;
|
|
12
|
+
export declare function slugify(text: string): string;
|
|
13
|
+
//# sourceMappingURL=headings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headings.d.ts","sourceRoot":"","sources":["../src/headings.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAc,MAAM,gBAAgB,CAAC;AAEpE,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,OAAO,EAAE,CA6B3D;AAED,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAWlD;AAyBD,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAU5C"}
|
package/dist/headings.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export function extractHeadings(tree) {
|
|
2
|
+
const headings = [];
|
|
3
|
+
const seen = new Map();
|
|
4
|
+
function walk(nodes) {
|
|
5
|
+
for (const node of nodes) {
|
|
6
|
+
if (node.type === "heading") {
|
|
7
|
+
const level = node.props?.level ?? 1;
|
|
8
|
+
const text = extractText(node);
|
|
9
|
+
let slug = node.props?.slug;
|
|
10
|
+
if (!slug) {
|
|
11
|
+
const base = slugify(text);
|
|
12
|
+
const count = seen.get(base) ?? 0;
|
|
13
|
+
slug = count === 0 ? base : `${base}-${count}`;
|
|
14
|
+
seen.set(base, count + 1);
|
|
15
|
+
}
|
|
16
|
+
if (!node.props)
|
|
17
|
+
node.props = {};
|
|
18
|
+
node.props.slug = slug;
|
|
19
|
+
node.props.text = text;
|
|
20
|
+
headings.push({ depth: level, slug, text });
|
|
21
|
+
}
|
|
22
|
+
if (node.children)
|
|
23
|
+
walk(node.children);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
walk(tree);
|
|
27
|
+
return headings;
|
|
28
|
+
}
|
|
29
|
+
/** Extract plain text from a node, preferring structured inline content. */
|
|
30
|
+
export function extractText(node) {
|
|
31
|
+
const parts = [];
|
|
32
|
+
if (node.inline) {
|
|
33
|
+
parts.push(extractInlineText(node.inline));
|
|
34
|
+
}
|
|
35
|
+
else if (node.html) {
|
|
36
|
+
parts.push(node.html.replace(/<[^>]+>/g, ""));
|
|
37
|
+
}
|
|
38
|
+
if (node.children) {
|
|
39
|
+
for (const child of node.children)
|
|
40
|
+
parts.push(extractText(child));
|
|
41
|
+
}
|
|
42
|
+
return parts.filter(Boolean).join(" ").trim();
|
|
43
|
+
}
|
|
44
|
+
function extractInlineText(nodes) {
|
|
45
|
+
const parts = [];
|
|
46
|
+
for (const node of nodes) {
|
|
47
|
+
switch (node.type) {
|
|
48
|
+
case "text":
|
|
49
|
+
case "code":
|
|
50
|
+
parts.push(node.text);
|
|
51
|
+
break;
|
|
52
|
+
case "link":
|
|
53
|
+
case "highlight":
|
|
54
|
+
parts.push(extractInlineText(node.children));
|
|
55
|
+
break;
|
|
56
|
+
case "image":
|
|
57
|
+
if (node.alt)
|
|
58
|
+
parts.push(node.alt);
|
|
59
|
+
break;
|
|
60
|
+
case "html-inline":
|
|
61
|
+
parts.push(node.html.replace(/<[^>]+>/g, ""));
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return parts.join("");
|
|
66
|
+
}
|
|
67
|
+
export function slugify(text) {
|
|
68
|
+
return (text
|
|
69
|
+
.toLowerCase()
|
|
70
|
+
.replace(/[^\w\s-]/g, "")
|
|
71
|
+
.replace(/\s+/g, "-")
|
|
72
|
+
.replace(/-+/g, "-")
|
|
73
|
+
.replace(/^-+|-+$/g, "")
|
|
74
|
+
.trim() || "section");
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=headings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headings.js","sourceRoot":"","sources":["../src/headings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { TreeNode } from "@dogsbay/types";
|
|
2
|
+
import { type InlineOptions } from "./inline-html.js";
|
|
3
|
+
export interface HtmlToTreeResult {
|
|
4
|
+
tree: TreeNode[];
|
|
5
|
+
/** Topic title from h1.topictitle1, if present. */
|
|
6
|
+
title?: string;
|
|
7
|
+
/** Derived from the body class: concept | task | reference | topic. */
|
|
8
|
+
topicType: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function htmlToTree(html: string, opts?: InlineOptions): HtmlToTreeResult;
|
|
11
|
+
/**
|
|
12
|
+
* Extract a YouTube video id from an embed/watch/short URL, else undefined.
|
|
13
|
+
* Handles youtube.com/embed/<id>, youtube-nocookie.com/embed/<id>,
|
|
14
|
+
* youtu.be/<id>, and watch?v=<id>.
|
|
15
|
+
*/
|
|
16
|
+
export declare function youtubeIdFromUrl(url: string): string | undefined;
|
|
17
|
+
//# sourceMappingURL=html-to-tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-to-tree.d.ts","sourceRoot":"","sources":["../src/html-to-tree.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAiD,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAErG,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;CACnB;AAWD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,gBAAgB,CAYnF;AAyBD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKhE"}
|