@metaobjectsdev/docs-site 0.15.8-rc.1
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/LICENSE +189 -0
- package/README.md +62 -0
- package/assets/site.css +13 -0
- package/assets/site.js +25 -0
- package/dist/badges.d.ts +14 -0
- package/dist/badges.d.ts.map +1 -0
- package/dist/badges.js +21 -0
- package/dist/badges.js.map +1 -0
- package/dist/builders/extras.d.ts +23 -0
- package/dist/builders/extras.d.ts.map +1 -0
- package/dist/builders/extras.js +70 -0
- package/dist/builders/extras.js.map +1 -0
- package/dist/builders/index-data.d.ts +52 -0
- package/dist/builders/index-data.d.ts.map +1 -0
- package/dist/builders/index-data.js +115 -0
- package/dist/builders/index-data.js.map +1 -0
- package/dist/builders/object-data.d.ts +97 -0
- package/dist/builders/object-data.d.ts.map +1 -0
- package/dist/builders/object-data.js +336 -0
- package/dist/builders/object-data.js.map +1 -0
- package/dist/builders/output-data.d.ts +26 -0
- package/dist/builders/output-data.d.ts.map +1 -0
- package/dist/builders/output-data.js +35 -0
- package/dist/builders/output-data.js.map +1 -0
- package/dist/builders/package-data.d.ts +48 -0
- package/dist/builders/package-data.d.ts.map +1 -0
- package/dist/builders/package-data.js +142 -0
- package/dist/builders/package-data.js.map +1 -0
- package/dist/builders/prompt-data.d.ts +31 -0
- package/dist/builders/prompt-data.d.ts.map +1 -0
- package/dist/builders/prompt-data.js +68 -0
- package/dist/builders/prompt-data.js.map +1 -0
- package/dist/coverage.d.ts +19 -0
- package/dist/coverage.d.ts.map +1 -0
- package/dist/coverage.js +26 -0
- package/dist/coverage.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/link-check.d.ts +2 -0
- package/dist/link-check.d.ts.map +1 -0
- package/dist/link-check.js +30 -0
- package/dist/link-check.js.map +1 -0
- package/dist/link-graph.d.ts +54 -0
- package/dist/link-graph.d.ts.map +1 -0
- package/dist/link-graph.js +198 -0
- package/dist/link-graph.js.map +1 -0
- package/dist/load.d.ts +11 -0
- package/dist/load.d.ts.map +1 -0
- package/dist/load.js +40 -0
- package/dist/load.js.map +1 -0
- package/dist/mermaid.d.ts +56 -0
- package/dist/mermaid.d.ts.map +1 -0
- package/dist/mermaid.js +136 -0
- package/dist/mermaid.js.map +1 -0
- package/dist/mustache-highlight.d.ts +10 -0
- package/dist/mustache-highlight.d.ts.map +1 -0
- package/dist/mustache-highlight.js +55 -0
- package/dist/mustache-highlight.js.map +1 -0
- package/dist/package-docs.d.ts +12 -0
- package/dist/package-docs.d.ts.map +1 -0
- package/dist/package-docs.js +34 -0
- package/dist/package-docs.js.map +1 -0
- package/dist/scaffold.d.ts +7 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +24 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/site.d.ts +23 -0
- package/dist/site.d.ts.map +1 -0
- package/dist/site.js +219 -0
- package/dist/site.js.map +1 -0
- package/dist/yaml-comments.d.ts +6 -0
- package/dist/yaml-comments.d.ts.map +1 -0
- package/dist/yaml-comments.js +49 -0
- package/dist/yaml-comments.js.map +1 -0
- package/package.json +45 -0
- package/src/badges.ts +26 -0
- package/src/builders/extras.ts +61 -0
- package/src/builders/index-data.ts +95 -0
- package/src/builders/object-data.ts +261 -0
- package/src/builders/output-data.ts +35 -0
- package/src/builders/package-data.ts +134 -0
- package/src/builders/prompt-data.ts +61 -0
- package/src/coverage.ts +29 -0
- package/src/index.ts +3 -0
- package/src/link-check.ts +24 -0
- package/src/link-graph.ts +204 -0
- package/src/load.ts +47 -0
- package/src/mermaid.ts +142 -0
- package/src/mustache-highlight.ts +43 -0
- package/src/package-docs.ts +33 -0
- package/src/scaffold.ts +26 -0
- package/src/site.ts +289 -0
- package/src/yaml-comments.ts +33 -0
- package/templates/chrome-foot.mustache +17 -0
- package/templates/chrome-head.mustache +21 -0
- package/templates/coverage.html.mustache +24 -0
- package/templates/enums.html.mustache +14 -0
- package/templates/index.html.mustache +54 -0
- package/templates/object.html.mustache +46 -0
- package/templates/output.html.mustache +16 -0
- package/templates/package.html.mustache +43 -0
- package/templates/prompt.html.mustache +28 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-docs.js","sourceRoot":"","sources":["../src/package-docs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAIhD,MAAM,UAAU,kBAAkB,CAAC,UAAoB;IACrD,MAAM,GAAG,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC1C,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,EAAE;QACzB,KAAK,MAAM,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/B,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACrB,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;gBAAE,IAAI,CAAC,CAAC,CAAC,CAAC;iBAClC,IAAI,CAAC,KAAK,eAAe,EAAE,CAAC;gBAC/B,IAAI,CAAC;oBACH,MAAM,CAAC,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAA8E,CAAC;oBACtH,MAAM,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC;oBACtB,IAAI,CAAC,EAAE,OAAO;wBAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;gBACjH,CAAC;gBAAC,MAAM,CAAC,CAAC,iFAAiF,CAAC,CAAC;YAC/F,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,UAAU;QAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACpC,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,CAAY,EAAE,CAAC,GAAG,CAAC;IAC1D,OAAO,CAAC,CAAC,KAAK,EAAE;SACb,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,QAAQ,IAAI,EAAE,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC;SAC7E,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;SAC7H,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;SACrE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** The 9 mustache templates the site is built from (basenames under templates/). */
|
|
2
|
+
export declare const SITE_TEMPLATE_NAMES: readonly string[];
|
|
3
|
+
/** The site's themeable assets (basenames under assets/). search-index.json is generated, not themed. */
|
|
4
|
+
export declare const SITE_ASSET_NAMES: readonly string[];
|
|
5
|
+
/** Read a bundled template or asset by basename (for scaffolding into a consumer). */
|
|
6
|
+
export declare function readSiteFile(kind: "template" | "asset", name: string): string;
|
|
7
|
+
//# sourceMappingURL=scaffold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAIA,oFAAoF;AACpF,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAUhD,CAAC;AAEF,yGAAyG;AACzG,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAA4B,CAAC;AAE3E,sFAAsF;AACtF,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAI7E"}
|
package/dist/scaffold.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
/** The 9 mustache templates the site is built from (basenames under templates/). */
|
|
5
|
+
export const SITE_TEMPLATE_NAMES = [
|
|
6
|
+
"chrome-head.mustache",
|
|
7
|
+
"chrome-foot.mustache",
|
|
8
|
+
"index.html.mustache",
|
|
9
|
+
"package.html.mustache",
|
|
10
|
+
"object.html.mustache",
|
|
11
|
+
"prompt.html.mustache",
|
|
12
|
+
"output.html.mustache",
|
|
13
|
+
"enums.html.mustache",
|
|
14
|
+
"coverage.html.mustache",
|
|
15
|
+
];
|
|
16
|
+
/** The site's themeable assets (basenames under assets/). search-index.json is generated, not themed. */
|
|
17
|
+
export const SITE_ASSET_NAMES = ["site.css", "site.js"];
|
|
18
|
+
/** Read a bundled template or asset by basename (for scaffolding into a consumer). */
|
|
19
|
+
export function readSiteFile(kind, name) {
|
|
20
|
+
const selfDir = dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
const dir = resolve(selfDir, kind === "template" ? "../templates" : "../assets");
|
|
22
|
+
return readFileSync(join(dir, name), "utf8");
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=scaffold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,oFAAoF;AACpF,MAAM,CAAC,MAAM,mBAAmB,GAAsB;IACpD,sBAAsB;IACtB,sBAAsB;IACtB,qBAAqB;IACrB,uBAAuB;IACvB,sBAAsB;IACtB,sBAAsB;IACtB,sBAAsB;IACtB,qBAAqB;IACrB,wBAAwB;CACzB,CAAC;AAEF,yGAAyG;AACzG,MAAM,CAAC,MAAM,gBAAgB,GAAsB,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;AAE3E,sFAAsF;AACtF,MAAM,UAAU,YAAY,CAAC,IAA0B,EAAE,IAAY;IACnE,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACxD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,EAAE,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IACjF,OAAO,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;AAC/C,CAAC"}
|
package/dist/site.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { CoverageReport } from "./coverage";
|
|
2
|
+
import type { CoreConfig } from "./builders/index-data";
|
|
3
|
+
import type { Anomaly } from "./builders/extras";
|
|
4
|
+
export interface SiteOptions {
|
|
5
|
+
sourceDirs: string[];
|
|
6
|
+
outDir: string;
|
|
7
|
+
title: string;
|
|
8
|
+
stamp: string;
|
|
9
|
+
commit: string;
|
|
10
|
+
core?: CoreConfig;
|
|
11
|
+
/** Override dir; if a file of the same basename exists here, it wins over the bundled templates/ dir. */
|
|
12
|
+
templatesDir?: string;
|
|
13
|
+
/** Override dir for assets; if a file of the same basename exists here, it wins over the bundled assets/ dir. */
|
|
14
|
+
assetsDir?: string | undefined;
|
|
15
|
+
}
|
|
16
|
+
export interface SiteResult {
|
|
17
|
+
pages: string[];
|
|
18
|
+
coverage: CoverageReport;
|
|
19
|
+
anomalies: Anomaly[];
|
|
20
|
+
dangling: string[];
|
|
21
|
+
}
|
|
22
|
+
export declare function generateSite(opts: SiteOptions): Promise<SiteResult>;
|
|
23
|
+
//# sourceMappingURL=site.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../src/site.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAMxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AASjD,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,yGAAyG;IACzG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iHAAiH;IACjH,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,cAAc,CAAC;IACzB,SAAS,EAAE,OAAO,EAAE,CAAC;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAwFD,wBAAsB,YAAY,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CA6JzE"}
|
package/dist/site.js
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { render, InMemoryProvider } from "@metaobjectsdev/render";
|
|
5
|
+
import { loadModel } from "./load";
|
|
6
|
+
import { LinkGraph, fqnOf } from "./link-graph";
|
|
7
|
+
import { CoverageTracker } from "./coverage";
|
|
8
|
+
import { harvestComments } from "./yaml-comments";
|
|
9
|
+
import { buildIndexPage } from "./builders/index-data";
|
|
10
|
+
import { buildPackagePage } from "./builders/package-data";
|
|
11
|
+
import { buildObjectPage } from "./builders/object-data";
|
|
12
|
+
import { buildPromptPage } from "./builders/prompt-data";
|
|
13
|
+
import { buildOutputPage } from "./builders/output-data";
|
|
14
|
+
import { buildEnumsPage, findAnomalies, buildSearchIndex } from "./builders/extras";
|
|
15
|
+
import { checkLinks } from "./link-check";
|
|
16
|
+
import { legendHtml, esc as escBadge } from "./badges";
|
|
17
|
+
// ─── Helpers ───────────────────────────────────────────────────────────────────
|
|
18
|
+
/** Relative-root prefix: "" for root pages, "../../" for depth-2 pages. */
|
|
19
|
+
function relRootFor(href) {
|
|
20
|
+
const depth = href.split("/").length - 1;
|
|
21
|
+
return "../".repeat(depth);
|
|
22
|
+
}
|
|
23
|
+
/** Write a file, creating parent dirs as needed. */
|
|
24
|
+
function writeOut(outDir, relPath, content) {
|
|
25
|
+
const abs = join(outDir, relPath);
|
|
26
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
27
|
+
writeFileSync(abs, content);
|
|
28
|
+
return relPath;
|
|
29
|
+
}
|
|
30
|
+
// ─── Template loading ──────────────────────────────────────────────────────────
|
|
31
|
+
const SELF_DIR = dirname(fileURLToPath(import.meta.url));
|
|
32
|
+
const BUNDLED_TEMPLATES = resolve(SELF_DIR, "../templates");
|
|
33
|
+
function loadTemplate(name, overrideDir) {
|
|
34
|
+
if (overrideDir) {
|
|
35
|
+
const candidate = join(overrideDir, name);
|
|
36
|
+
if (existsSync(candidate))
|
|
37
|
+
return readFileSync(candidate, "utf8");
|
|
38
|
+
}
|
|
39
|
+
return readFileSync(join(BUNDLED_TEMPLATES, name), "utf8");
|
|
40
|
+
}
|
|
41
|
+
const BUNDLED_ASSETS = resolve(SELF_DIR, "../assets");
|
|
42
|
+
function loadAsset(name, overrideDir) {
|
|
43
|
+
if (overrideDir) {
|
|
44
|
+
const candidate = join(overrideDir, name);
|
|
45
|
+
if (existsSync(candidate))
|
|
46
|
+
return readFileSync(candidate, "utf8");
|
|
47
|
+
}
|
|
48
|
+
return readFileSync(join(BUNDLED_ASSETS, name), "utf8");
|
|
49
|
+
}
|
|
50
|
+
// ─── Nav HTML ─────────────────────────────────────────────────────────────────
|
|
51
|
+
/**
|
|
52
|
+
* Build the sidebar nav HTML for a specific page as a collapsible tree.
|
|
53
|
+
* Each package is a <details> element (open if it's the current page's package).
|
|
54
|
+
* Members are listed as links within each package. Deterministic: sorted packages, sorted members.
|
|
55
|
+
*/
|
|
56
|
+
function buildNavHtml(g, relRoot, currentPkgPath, dataPackages, promptPackages) {
|
|
57
|
+
const nodes = g.nodes();
|
|
58
|
+
const renderGroup = (label, pkgList) => {
|
|
59
|
+
if (pkgList.length === 0)
|
|
60
|
+
return "";
|
|
61
|
+
const parts = [`<div class="text-xs font-semibold opacity-50 mt-3 mb-1">${label}</div>`];
|
|
62
|
+
for (const p of pkgList) {
|
|
63
|
+
const isOpen = p.pkgPath === currentPkgPath;
|
|
64
|
+
const pkgHref = escBadge(relRoot + p.pkgPath + "/index.html");
|
|
65
|
+
const pkgLabel = escBadge(p.pkg);
|
|
66
|
+
const members = nodes
|
|
67
|
+
.filter((n) => n.pkg === p.pkg)
|
|
68
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
69
|
+
const memberLinks = members.map((m) => `<a href="${escBadge(relRoot + m.href)}" class="link font-mono text-xs opacity-70 hover:opacity-100 pl-3 block">${escBadge(m.name)}</a>`).join("\n");
|
|
70
|
+
parts.push(`<details${isOpen ? " open" : ""}>\n` +
|
|
71
|
+
`<summary class="cursor-pointer font-mono text-xs opacity-70 hover:opacity-100"><a href="${pkgHref}">${pkgLabel}</a></summary>\n` +
|
|
72
|
+
memberLinks + `\n</details>`);
|
|
73
|
+
}
|
|
74
|
+
return parts.join("\n");
|
|
75
|
+
};
|
|
76
|
+
return [
|
|
77
|
+
renderGroup("Data", dataPackages),
|
|
78
|
+
renderGroup("Prompts", promptPackages),
|
|
79
|
+
].filter(Boolean).join("\n");
|
|
80
|
+
}
|
|
81
|
+
// ─── Orchestrator ─────────────────────────────────────────────────────────────
|
|
82
|
+
export async function generateSite(opts) {
|
|
83
|
+
// 1. Load + graph + comments
|
|
84
|
+
const loaded = await loadModel(opts.sourceDirs);
|
|
85
|
+
const g = new LinkGraph(loaded);
|
|
86
|
+
const docs = harvestComments(opts.sourceDirs);
|
|
87
|
+
const cov = new CoverageTracker();
|
|
88
|
+
// 2. Derive package lists (deterministic sort)
|
|
89
|
+
const nodes = g.nodes();
|
|
90
|
+
const allPkgs = [...new Set(nodes.map((n) => n.pkg))].sort();
|
|
91
|
+
const promptPkgSet = new Set(nodes.filter((n) => n.kind !== "object").map((n) => n.pkg));
|
|
92
|
+
const dataPkgList = allPkgs
|
|
93
|
+
.filter((p) => !promptPkgSet.has(p))
|
|
94
|
+
.map((p) => ({ pkg: p, pkgPath: p.split("::").join("/") }));
|
|
95
|
+
const promptPkgList = allPkgs
|
|
96
|
+
.filter((p) => promptPkgSet.has(p))
|
|
97
|
+
.map((p) => ({ pkg: p, pkgPath: p.split("::").join("/") }));
|
|
98
|
+
// 3. Load chrome templates (fixed)
|
|
99
|
+
const chromeHead = loadTemplate("chrome-head.mustache", opts.templatesDir);
|
|
100
|
+
const chromeFoot = loadTemplate("chrome-foot.mustache", opts.templatesDir);
|
|
101
|
+
// 4. Rendering helper: concat chrome + page template, render with payload + shared fields
|
|
102
|
+
const provider = new InMemoryProvider({});
|
|
103
|
+
const pages = [];
|
|
104
|
+
const legend = legendHtml();
|
|
105
|
+
// TOC builders — only list sections with non-empty data; ids must match id="s-..." in templates
|
|
106
|
+
const objectTocHtml = (d) => {
|
|
107
|
+
const sections = [
|
|
108
|
+
{ id: "s-overview", label: "Overview", present: !!d.desc },
|
|
109
|
+
{ id: "s-fields", label: "Fields", present: d.ownFields.length > 0 },
|
|
110
|
+
{ id: "s-indexes", label: "Indexes & keys", present: d.indexes.length > 0 },
|
|
111
|
+
{ id: "s-validators", label: "Validators", present: d.validators.length > 0 },
|
|
112
|
+
{ id: "s-relationships", label: "Relationships", present: d.relations.length > 0 },
|
|
113
|
+
{ id: "s-provenance", label: "Field provenance", present: d.origins.length > 0 },
|
|
114
|
+
{ id: "s-inheritance", label: "Inheritance", present: !!d.inheritanceMermaid },
|
|
115
|
+
{ id: "s-neighborhood", label: "Neighborhood", present: !!d.neighborhoodMermaid },
|
|
116
|
+
{ id: "s-referenced-by", label: "Referenced by", present: d.referencedBy.length > 0 },
|
|
117
|
+
];
|
|
118
|
+
return sections
|
|
119
|
+
.filter((s) => s.present)
|
|
120
|
+
.map((s) => `<a href="#${s.id}" class="link opacity-70 hover:opacity-100">${s.label}</a>`)
|
|
121
|
+
.join("\n");
|
|
122
|
+
};
|
|
123
|
+
const promptTocHtml = (d) => {
|
|
124
|
+
const sections = [
|
|
125
|
+
{ id: "s-payload", label: "Payload tree", present: d.payloadTree.length > 0 },
|
|
126
|
+
{ id: "s-source", label: "Source", present: !!d.sourceHtml },
|
|
127
|
+
];
|
|
128
|
+
return sections
|
|
129
|
+
.filter((s) => s.present)
|
|
130
|
+
.map((s) => `<a href="#${s.id}" class="link opacity-70 hover:opacity-100">${s.label}</a>`)
|
|
131
|
+
.join("\n");
|
|
132
|
+
};
|
|
133
|
+
const outputTocHtml = (d) => {
|
|
134
|
+
const sections = [
|
|
135
|
+
{ id: "s-contract", label: "Parse contract", present: d.fields.length > 0 },
|
|
136
|
+
];
|
|
137
|
+
return sections
|
|
138
|
+
.filter((s) => s.present)
|
|
139
|
+
.map((s) => `<a href="#${s.id}" class="link opacity-70 hover:opacity-100">${s.label}</a>`)
|
|
140
|
+
.join("\n");
|
|
141
|
+
};
|
|
142
|
+
const renderPage = (pageTemplateName, href, pageData, tocHtml = "", currentPkgPath = "") => {
|
|
143
|
+
const relRoot = relRootFor(href);
|
|
144
|
+
const navHtml = buildNavHtml(g, relRoot, currentPkgPath, dataPkgList, promptPkgList);
|
|
145
|
+
const pageTpl = loadTemplate(pageTemplateName, opts.templatesDir);
|
|
146
|
+
const template = chromeHead + pageTpl + chromeFoot;
|
|
147
|
+
const payload = {
|
|
148
|
+
...pageData,
|
|
149
|
+
title: opts.title,
|
|
150
|
+
stamp: opts.stamp,
|
|
151
|
+
commit: opts.commit,
|
|
152
|
+
relRoot,
|
|
153
|
+
navHtml,
|
|
154
|
+
tocHtml,
|
|
155
|
+
legendHtml: legend,
|
|
156
|
+
};
|
|
157
|
+
const html = render({ template, payload, provider, format: "text" });
|
|
158
|
+
writeOut(opts.outDir, href, html);
|
|
159
|
+
pages.push(href);
|
|
160
|
+
};
|
|
161
|
+
// 5. Index page
|
|
162
|
+
const indexData = buildIndexPage(g, cov, {
|
|
163
|
+
title: opts.title,
|
|
164
|
+
stamp: opts.stamp,
|
|
165
|
+
commit: opts.commit,
|
|
166
|
+
core: opts.core,
|
|
167
|
+
sourceDirs: opts.sourceDirs,
|
|
168
|
+
});
|
|
169
|
+
renderPage("index.html.mustache", "index.html", indexData);
|
|
170
|
+
// 6. Enums page
|
|
171
|
+
const enumRows = buildEnumsPage(g);
|
|
172
|
+
renderPage("enums.html.mustache", "enums.html", { rows: enumRows });
|
|
173
|
+
// 7. Anomalies + coverage (deferred until all pages built — build now, write after member pages)
|
|
174
|
+
const anomalies = findAnomalies(g, opts.sourceDirs);
|
|
175
|
+
// 8. Package + member pages (deterministic: pkgs sorted, then members sorted)
|
|
176
|
+
for (const pkg of allPkgs) {
|
|
177
|
+
const pkgData = buildPackagePage(pkg, g, cov, opts.sourceDirs);
|
|
178
|
+
const pkgHref = `${pkgData.pkgPath}/index.html`;
|
|
179
|
+
renderPage("package.html.mustache", pkgHref, pkgData, "", pkgData.pkgPath);
|
|
180
|
+
// Member pages sorted by name
|
|
181
|
+
const pkgNodes = nodes.filter((n) => n.pkg === pkg).sort((a, b) => a.name.localeCompare(b.name));
|
|
182
|
+
for (const n of pkgNodes) {
|
|
183
|
+
const fqn = fqnOf(n.node);
|
|
184
|
+
const pkgPath = n.pkgPath;
|
|
185
|
+
if (n.kind === "object") {
|
|
186
|
+
const data = buildObjectPage(fqn, g, cov);
|
|
187
|
+
renderPage("object.html.mustache", n.href, data, objectTocHtml(data), pkgPath);
|
|
188
|
+
}
|
|
189
|
+
else if (n.kind === "prompt") {
|
|
190
|
+
const data = buildPromptPage(fqn, g, cov, opts.sourceDirs);
|
|
191
|
+
renderPage("prompt.html.mustache", n.href, data, promptTocHtml(data), pkgPath);
|
|
192
|
+
}
|
|
193
|
+
else if (n.kind === "output") {
|
|
194
|
+
const data = buildOutputPage(fqn, g, cov, docs, opts.sourceDirs);
|
|
195
|
+
renderPage("output.html.mustache", n.href, data, outputTocHtml(data), pkgPath);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// 9. Coverage page (after all member pages so cov is fully populated)
|
|
200
|
+
const coverage = cov.report(loaded.root);
|
|
201
|
+
const coverageData = {
|
|
202
|
+
kinds: coverage.kinds,
|
|
203
|
+
attrs: coverage.attrs,
|
|
204
|
+
anomalies,
|
|
205
|
+
};
|
|
206
|
+
renderPage("coverage.html.mustache", "coverage.html", coverageData);
|
|
207
|
+
// 10. Write assets (consumer assetsDir wins over the bundled dir)
|
|
208
|
+
writeOut(opts.outDir, "assets/site.css", loadAsset("site.css", opts.assetsDir));
|
|
209
|
+
writeOut(opts.outDir, "assets/site.js", loadAsset("site.js", opts.assetsDir));
|
|
210
|
+
// 11. Search index
|
|
211
|
+
const searchIndex = buildSearchIndex(g);
|
|
212
|
+
writeOut(opts.outDir, "assets/search-index.json", JSON.stringify(searchIndex));
|
|
213
|
+
// 12. Link check (throws if any dangling links found)
|
|
214
|
+
const dangling = checkLinks(opts.outDir, pages);
|
|
215
|
+
if (dangling.length)
|
|
216
|
+
throw new Error("link check failed:\n" + dangling.join("\n"));
|
|
217
|
+
return { pages, coverage, anomalies, dangling };
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=site.js.map
|
package/dist/site.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"site.js","sourceRoot":"","sources":["../src/site.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"yaml-comments.d.ts","sourceRoot":"","sources":["../src/yaml-comments.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAAG,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAAE;AAGjG,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,WAAW,CA0BjE"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
const clean = (c) => c.replace(/^[#\s─═-]+/, "").replace(/[─═]+\s*$/, "").trim();
|
|
4
|
+
export function harvestComments(sourceDirs) {
|
|
5
|
+
const objectDesc = new Map();
|
|
6
|
+
const fieldNote = new Map();
|
|
7
|
+
const files = [];
|
|
8
|
+
const walk = (d) => { for (const e of readdirSync(d)) {
|
|
9
|
+
const p = join(d, e);
|
|
10
|
+
if (statSync(p).isDirectory())
|
|
11
|
+
walk(p);
|
|
12
|
+
else if (/\.ya?ml$/.test(e))
|
|
13
|
+
files.push(p);
|
|
14
|
+
} };
|
|
15
|
+
for (const d of sourceDirs)
|
|
16
|
+
walk(d);
|
|
17
|
+
for (const f of files) {
|
|
18
|
+
const lines = readFileSync(f, "utf8").split("\n");
|
|
19
|
+
let current;
|
|
20
|
+
for (let i = 0; i < lines.length; i++) {
|
|
21
|
+
const m = lines[i].match(/^\s*-\s*object\.\w+:/);
|
|
22
|
+
if (m) {
|
|
23
|
+
current = undefined;
|
|
24
|
+
for (let j = i + 1; j < Math.min(i + 4, lines.length); j++) {
|
|
25
|
+
const nm = lines[j].match(/^\s*name:\s*(\S+)/);
|
|
26
|
+
if (nm) {
|
|
27
|
+
current = nm[1];
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (current) {
|
|
32
|
+
const desc = [];
|
|
33
|
+
for (let k = i - 1; k >= 0 && lines[k].trim().startsWith("#"); k--) {
|
|
34
|
+
const c = clean(lines[k].trim());
|
|
35
|
+
if (c)
|
|
36
|
+
desc.unshift(c);
|
|
37
|
+
}
|
|
38
|
+
if (desc.length)
|
|
39
|
+
objectDesc.set(current, desc.join(" ").replace(/\s+/g, " ").slice(0, 400));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const fm = lines[i].match(/^\s*-\s*field\.\w+:\s*\{\s*name:\s*(\w+)[^}]*\}\s*#\s*(.+)$/);
|
|
43
|
+
if (fm && current)
|
|
44
|
+
fieldNote.set(`${current}.${fm[1]}`, clean(fm[2]).slice(0, 160));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return { objectDesc, fieldNote };
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=yaml-comments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"yaml-comments.js","sourceRoot":"","sources":["../src/yaml-comments.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@metaobjectsdev/docs-site",
|
|
3
|
+
"version": "0.15.8-rc.1",
|
|
4
|
+
"description": "HTML documentation-site generator for metaobjects models — a browsable multi-page site (nav, search, per-object/package pages, kind-aware ER diagrams). Powers the `meta docs --site` surface.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"bun": "./src/index.ts",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": ["dist", "src", "templates", "assets", "README.md", "LICENSE"],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc -p .",
|
|
18
|
+
"typecheck": "tsc -p tsconfig.typecheck.json",
|
|
19
|
+
"test": "bun test"
|
|
20
|
+
},
|
|
21
|
+
"license": "Apache-2.0",
|
|
22
|
+
"author": "Doug Mealing <doug@dougmealing.com>",
|
|
23
|
+
"homepage": "https://metaobjects.dev",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/metaobjectsdev/metaobjects/issues"
|
|
26
|
+
},
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/metaobjectsdev/metaobjects.git",
|
|
30
|
+
"directory": "server/typescript/packages/docs-site"
|
|
31
|
+
},
|
|
32
|
+
"keywords": ["metaobjects", "docs", "documentation", "html", "site", "erd"],
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@metaobjectsdev/metadata": "0.15.8-rc.1",
|
|
38
|
+
"@metaobjectsdev/render": "0.15.8-rc.1",
|
|
39
|
+
"yaml": "^2.9.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"bun-types": "latest",
|
|
43
|
+
"typescript": "^5.6.0"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/badges.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const esc = (s: unknown) =>
|
|
2
|
+
String(s).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
3
|
+
|
|
4
|
+
export interface Badge { text: string; cls: string; href?: string; title?: string; }
|
|
5
|
+
|
|
6
|
+
export function badge(b: Badge): string {
|
|
7
|
+
const cls = `badge ${b.cls} badge-xs`;
|
|
8
|
+
const title = b.title ? ` title="${esc(b.title)}"` : "";
|
|
9
|
+
return b.href
|
|
10
|
+
? `<a href="${esc(b.href)}" class="${cls}"${title}>${esc(b.text)}</a>`
|
|
11
|
+
: `<span class="${cls}"${title}>${esc(b.text)}</span>`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const LEGEND: { label: string; cls: string }[] = [
|
|
15
|
+
{ label: "reference (fk)", cls: "badge-soft badge-info" },
|
|
16
|
+
{ label: "contains (nested)", cls: "badge-soft badge-secondary" },
|
|
17
|
+
{ label: "indexed / pk", cls: "badge-soft badge-success" },
|
|
18
|
+
{ label: "required", cls: "badge-soft badge-error" },
|
|
19
|
+
{ label: "deprecated", cls: "badge-soft badge-warning" },
|
|
20
|
+
{ label: "enum", cls: "badge-soft badge-accent" },
|
|
21
|
+
{ label: "optional", cls: "badge-soft badge-neutral" },
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
export function legendHtml(): string {
|
|
25
|
+
return LEGEND.map((l) => `<span class="badge ${l.cls} badge-xs">${esc(l.label)}</span>`).join(" ");
|
|
26
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { LinkGraph, fqnOf } from "../link-graph";
|
|
4
|
+
|
|
5
|
+
export interface EnumRow { owner: string; ownerHref: string; field: string; values: string[]; deflt: string; }
|
|
6
|
+
export function buildEnumsPage(g: LinkGraph): EnumRow[] {
|
|
7
|
+
const rows: EnumRow[] = [];
|
|
8
|
+
for (const o of g.nodes().filter((n) => n.kind === "object")) {
|
|
9
|
+
for (const f of o.node.childrenOfType("field")) {
|
|
10
|
+
const values = f.attr("values");
|
|
11
|
+
if (Array.isArray(values)) rows.push({ owner: o.name, ownerHref: o.href, field: f.name, values: values.map(String), deflt: String(f.attr("default") ?? "") });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return rows.sort((a, b) => (a.owner + a.field).localeCompare(b.owner + b.field));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface Anomaly { kind: string; subject: string; href: string; detail: string; }
|
|
18
|
+
export function findAnomalies(g: LinkGraph, sourceDirs: string[]): Anomaly[] {
|
|
19
|
+
const out: Anomaly[] = [];
|
|
20
|
+
const objs = g.nodes().filter((n) => n.kind === "object");
|
|
21
|
+
for (const o of objs) {
|
|
22
|
+
const fqn = fqnOf(o.node);
|
|
23
|
+
if (!o.node.isAbstract && g.degree(fqn) === 0) out.push({ kind: "orphan", subject: o.name, href: o.href, detail: "no inbound or outbound references" });
|
|
24
|
+
if (o.node.isAbstract && g.extendedBy(fqn).length === 0) out.push({ kind: "unextended-abstract", subject: o.name, href: o.href, detail: "abstract with no descendants" });
|
|
25
|
+
const fkFields = new Set<string>();
|
|
26
|
+
for (const i of o.node.childrenOfType("identity").filter((i) => i.subType === "reference")) {
|
|
27
|
+
const fv = i.attr("fields");
|
|
28
|
+
const names = Array.isArray(fv) ? fv.map(String) : [String(fv ?? "")];
|
|
29
|
+
for (const n of names) fkFields.add(n);
|
|
30
|
+
}
|
|
31
|
+
for (const f of o.node.childrenOfType("field")) {
|
|
32
|
+
if (/(^|[a-z])Id$/.test(f.name) && (f.subType === "string" || f.subType === "uuid") && f.attr("objectRef") === undefined && !fkFields.has(f.name) && !o.node.isAbstract)
|
|
33
|
+
out.push({ kind: "implied-ref", subject: `${o.name}.${f.name}`, href: `${o.href}#f-${f.name}`, detail: "looks like a reference but declares none" });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// unreachable payload VOs: object.value not reached from any template payload tree
|
|
37
|
+
const reachable = new Set<string>();
|
|
38
|
+
for (const t of g.nodes().filter((n) => n.kind !== "object")) {
|
|
39
|
+
const q = g.refsFrom(fqnOf(t.node)).filter((r) => r.kind === "payload").map((r) => r.to);
|
|
40
|
+
while (q.length) { const cur = q.shift()!; if (reachable.has(cur)) continue; reachable.add(cur); for (const r of g.refsFrom(cur)) if (r.kind === "field") q.push(r.to); }
|
|
41
|
+
}
|
|
42
|
+
for (const o of objs.filter((o) => o.node.subType === "value" && g.nodes().some((t) => t.kind !== "object" && t.pkg === o.pkg)))
|
|
43
|
+
if (!reachable.has(fqnOf(o.node)) && g.degree(fqnOf(o.node)) === 0)
|
|
44
|
+
out.push({ kind: "unreachable-vo", subject: o.name, href: o.href, detail: "value object not reachable from any template payload" });
|
|
45
|
+
for (const t of g.nodes().filter((n) => n.kind !== "object")) {
|
|
46
|
+
const ref = String(t.node.attr("textRef") ?? "");
|
|
47
|
+
if (ref && !sourceDirs.some((d) => existsSync(join(d, ...ref.split("/")) + ".mustache")))
|
|
48
|
+
out.push({ kind: "unresolved-textref", subject: t.name, href: t.href, detail: `@textRef ${ref} does not resolve (forward-pointing)` });
|
|
49
|
+
}
|
|
50
|
+
return out.sort((a, b) => (a.kind + a.subject).localeCompare(b.kind + b.subject));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface SearchEntry { t: string; h: string; k: "object" | "prompt" | "output" | "field"; }
|
|
54
|
+
export function buildSearchIndex(g: LinkGraph): SearchEntry[] {
|
|
55
|
+
const out: SearchEntry[] = [];
|
|
56
|
+
for (const n of g.nodes()) {
|
|
57
|
+
out.push({ t: `${n.pkg}::${n.name}`, h: n.href, k: n.kind });
|
|
58
|
+
if (n.kind === "object") for (const f of n.node.childrenOfType("field")) out.push({ t: `${n.name}.${f.name}`, h: `${n.href}#f-${f.name}`, k: "field" });
|
|
59
|
+
}
|
|
60
|
+
return out.sort((a, b) => a.t.localeCompare(b.t));
|
|
61
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { LinkGraph, fqnOf } from "../link-graph";
|
|
2
|
+
import type { CoverageTracker } from "../coverage";
|
|
3
|
+
import { flowchartDomain, packageFlowchart } from "../mermaid";
|
|
4
|
+
import { harvestPackageDocs } from "../package-docs";
|
|
5
|
+
import { esc } from "../badges";
|
|
6
|
+
|
|
7
|
+
export interface PkgCard { pkg: string; href: string; objectCount: number; promptCount: number; contractCount: number; purpose: string; }
|
|
8
|
+
export interface CoreConfig { pin?: string[]; exclude?: string[]; n?: number; }
|
|
9
|
+
export interface IndexPageData { title: string; stamp: string; commit: string; stats: { objects: number; tables: number; packages: number; promptVos: number; prompts: number; contracts: number; enums: number }; coreMermaid: string; coreCaption: string; coreLegend: { pkg: string; fill: string; stroke: string }[]; packageMermaid: string; fullEdges: { from: string; to: string; n: number }[]; dataPackages: PkgCard[]; promptPackages: PkgCard[]; }
|
|
10
|
+
|
|
11
|
+
export function buildIndexPage(g: LinkGraph, cov: CoverageTracker, opts: { title: string; stamp: string; commit: string; core?: CoreConfig | undefined; sourceDirs?: string[] | undefined }): IndexPageData {
|
|
12
|
+
const objs = g.nodes().filter((n) => n.kind === "object");
|
|
13
|
+
const tpls = g.nodes().filter((n) => n.kind !== "object");
|
|
14
|
+
const pkgs = [...new Set(g.nodes().map((n) => n.pkg))].sort();
|
|
15
|
+
const promptPkgSet = new Set(tpls.map((t) => t.pkg));
|
|
16
|
+
const shortPkg = (p: string) => p.split("::").pop()!;
|
|
17
|
+
// core map = a CONNECTED cluster of the most-connected objects of ALL kinds (entities, projections,
|
|
18
|
+
// value objects), traversing every edge type (fk, field.object, origin, extends, relationship).
|
|
19
|
+
// Seed by total degree, pull in the seeds' neighbors so nothing dangles, cap the total, drop isolates.
|
|
20
|
+
const CORE_MAX = opts.core?.n ?? 28;
|
|
21
|
+
const excluded = new Set(opts.core?.exclude ?? []);
|
|
22
|
+
const degOf = (fqn: string) => g.refsFrom(fqn).length + g.refsTo(fqn).length;
|
|
23
|
+
// seed a BALANCED mix so all object kinds appear (payload VOs otherwise dominate by degree):
|
|
24
|
+
// top entities (data-model backbone) + top value objects (payload structure) + projections (views).
|
|
25
|
+
const topByType = (st: string, k: number) => objs.filter((n) => !n.node.isAbstract && n.node.subType === st && !excluded.has(fqnOf(n.node)))
|
|
26
|
+
.map((dn) => ({ dn, fqn: fqnOf(dn.node), deg: degOf(fqnOf(dn.node)) }))
|
|
27
|
+
.sort((a, b) => b.deg - a.deg || a.dn.name.localeCompare(b.dn.name)).slice(0, k);
|
|
28
|
+
const seeds = [...topByType("entity", 8), ...topByType("value", 5), ...topByType("projection", 3)];
|
|
29
|
+
const shown = new Map<string, (typeof seeds)[0]["dn"]>();
|
|
30
|
+
// pinned objects are force-included (author override), never excluded.
|
|
31
|
+
for (const f of opts.core?.pin ?? []) {
|
|
32
|
+
if (excluded.has(f)) continue;
|
|
33
|
+
const dn = g.byFqn(f); if (dn && dn.kind === "object") shown.set(f, dn);
|
|
34
|
+
}
|
|
35
|
+
for (const s of seeds) { if (shown.size >= CORE_MAX) break; if (!shown.has(s.fqn)) shown.set(s.fqn, s.dn); }
|
|
36
|
+
// rank candidate neighbors by how many shown nodes touch them, add until the cap
|
|
37
|
+
const cand = new Map<string, number>();
|
|
38
|
+
for (const f of shown.keys()) {
|
|
39
|
+
for (const e of g.refsFrom(f)) cand.set(e.to, (cand.get(e.to) ?? 0) + 1);
|
|
40
|
+
for (const e of g.refsTo(f)) cand.set(e.from, (cand.get(e.from) ?? 0) + 1);
|
|
41
|
+
}
|
|
42
|
+
for (const [f] of [...cand.entries()].filter(([f]) => !shown.has(f) && !excluded.has(f)).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))) {
|
|
43
|
+
if (shown.size >= CORE_MAX) break;
|
|
44
|
+
const dn = g.byFqn(f); if (dn && dn.kind === "object") shown.set(f, dn);
|
|
45
|
+
}
|
|
46
|
+
// edges among the shown set — collapse parallel edges between a pair to ONE unlabeled edge
|
|
47
|
+
// (a projection can join a source object on 6-12 fields; the overview only needs the connection).
|
|
48
|
+
const heroEdges: { from: string; to: string; label: string; style?: "dashed" }[] = [];
|
|
49
|
+
const seenEdge = new Set<string>();
|
|
50
|
+
for (const [f, dn] of shown) for (const e of g.refsFrom(f)) if (shown.has(e.to)) {
|
|
51
|
+
const to = g.byFqn(e.to)!.name;
|
|
52
|
+
const key = `${dn.name}|${to}`;
|
|
53
|
+
if (!seenEdge.has(key)) { seenEdge.add(key); heroEdges.push({ from: dn.name, to, label: "", ...(e.cardinality === "many" ? { style: "dashed" as const } : {}) }); }
|
|
54
|
+
}
|
|
55
|
+
const connected = new Set(heroEdges.flatMap((e) => [e.from, e.to]));
|
|
56
|
+
const heroNodes = [...shown.values()].filter((dn) => connected.has(dn.name));
|
|
57
|
+
const hero = flowchartDomain(heroNodes.map((dn) => ({ name: dn.name, pkg: dn.pkg, kind: dn.node.subType })), heroEdges);
|
|
58
|
+
const coreMermaid = hero.mermaid, coreLegend = hero.legend;
|
|
59
|
+
const coreCaption = `${heroNodes.length} of the most-connected objects (entities, views, and payloads), colored by domain.`;
|
|
60
|
+
// package docs for purpose cards
|
|
61
|
+
const pdocs = harvestPackageDocs(opts.sourceDirs ?? []);
|
|
62
|
+
// package edges
|
|
63
|
+
const pkgEdges = new Map<string, number>();
|
|
64
|
+
for (const o of objs) for (const r of g.refsFrom(fqnOf(o.node))) {
|
|
65
|
+
if (r.kind === "extends") continue;
|
|
66
|
+
const t = g.byFqn(r.to); if (!t || t.pkg === o.pkg) continue;
|
|
67
|
+
const k = `${shortPkg(o.pkg)}→${shortPkg(t.pkg)}`; pkgEdges.set(k, (pkgEdges.get(k) ?? 0) + 1);
|
|
68
|
+
}
|
|
69
|
+
const fullEdges = [...pkgEdges.entries()].sort().map(([k, n]) => { const [from = "", to = ""] = k.split("→"); return { from, to, n }; });
|
|
70
|
+
const counts = new Map<string, number>(); for (const o of objs) counts.set(shortPkg(o.pkg), (counts.get(shortPkg(o.pkg)) ?? 0) + 1);
|
|
71
|
+
const card = (p: string): PkgCard => {
|
|
72
|
+
const doc = pdocs.get(p);
|
|
73
|
+
const purpose = esc((doc?.title ? doc.title + " — " : "") + (doc?.description ?? "")).slice(0, 160);
|
|
74
|
+
return { pkg: p, href: `${p.split("::").join("/")}/index.html`,
|
|
75
|
+
objectCount: objs.filter((o) => o.pkg === p).length,
|
|
76
|
+
promptCount: tpls.filter((t) => t.pkg === p && t.kind === "prompt").length,
|
|
77
|
+
contractCount: tpls.filter((t) => t.pkg === p && t.kind === "output").length,
|
|
78
|
+
purpose };
|
|
79
|
+
};
|
|
80
|
+
const enums = objs.reduce((n, o) => n + o.node.childrenOfType("field").filter((f) => Array.isArray(f.attr("values"))).length, 0);
|
|
81
|
+
return { title: opts.title, stamp: opts.stamp, commit: opts.commit,
|
|
82
|
+
stats: { objects: objs.length,
|
|
83
|
+
tables: objs.filter((o) => o.node.childrenOfType("source").length > 0).length,
|
|
84
|
+
packages: pkgs.length,
|
|
85
|
+
promptVos: objs.filter((o) => promptPkgSet.has(o.pkg)).length,
|
|
86
|
+
prompts: tpls.filter((t) => t.kind === "prompt").length,
|
|
87
|
+
contracts: tpls.filter((t) => t.kind === "output").length, enums },
|
|
88
|
+
coreMermaid,
|
|
89
|
+
coreCaption,
|
|
90
|
+
coreLegend,
|
|
91
|
+
packageMermaid: packageFlowchart(fullEdges.filter((e) => e.n >= 2), counts),
|
|
92
|
+
fullEdges,
|
|
93
|
+
dataPackages: pkgs.filter((p) => !promptPkgSet.has(p)).map(card),
|
|
94
|
+
promptPackages: pkgs.filter((p) => promptPkgSet.has(p)).map(card) };
|
|
95
|
+
}
|