@milehimikey/em-portal 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +231 -0
  3. package/dist/build.d.ts +21 -0
  4. package/dist/build.js +132 -0
  5. package/dist/build.js.map +1 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +43 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/crossModel.d.ts +14 -0
  10. package/dist/crossModel.js +49 -0
  11. package/dist/crossModel.js.map +1 -0
  12. package/dist/em/docBody.d.ts +5 -0
  13. package/dist/em/docBody.js +43 -0
  14. package/dist/em/docBody.js.map +1 -0
  15. package/dist/em/exportDoc.d.ts +135 -0
  16. package/dist/em/exportDoc.js +22 -0
  17. package/dist/em/exportDoc.js.map +1 -0
  18. package/dist/em/run.d.ts +41 -0
  19. package/dist/em/run.js +99 -0
  20. package/dist/em/run.js.map +1 -0
  21. package/dist/em/statusDoc.d.ts +56 -0
  22. package/dist/em/statusDoc.js +11 -0
  23. package/dist/em/statusDoc.js.map +1 -0
  24. package/dist/pages/html.d.ts +11 -0
  25. package/dist/pages/html.js +84 -0
  26. package/dist/pages/html.js.map +1 -0
  27. package/dist/pages/index.d.ts +3 -0
  28. package/dist/pages/index.js +128 -0
  29. package/dist/pages/index.js.map +1 -0
  30. package/dist/pages/model.d.ts +17 -0
  31. package/dist/pages/model.js +54 -0
  32. package/dist/pages/model.js.map +1 -0
  33. package/dist/pages/slice.d.ts +16 -0
  34. package/dist/pages/slice.js +144 -0
  35. package/dist/pages/slice.js.map +1 -0
  36. package/dist/pages/types.d.ts +22 -0
  37. package/dist/pages/types.js +4 -0
  38. package/dist/pages/types.js.map +1 -0
  39. package/dist/refs.d.ts +36 -0
  40. package/dist/refs.js +69 -0
  41. package/dist/refs.js.map +1 -0
  42. package/dist/slug.d.ts +5 -0
  43. package/dist/slug.js +30 -0
  44. package/dist/slug.js.map +1 -0
  45. package/package.json +60 -0
@@ -0,0 +1,135 @@
1
+ export interface EmGenerator {
2
+ name: string;
3
+ version: string;
4
+ }
5
+ export interface EmDiagnostic {
6
+ severity: "error" | "warning" | string;
7
+ code: string | null;
8
+ message: string;
9
+ line: number | null;
10
+ refs: string[];
11
+ }
12
+ export interface DocLineageRef {
13
+ raw: string;
14
+ sliceKey: string | null;
15
+ version: number | null;
16
+ }
17
+ export interface SliceDocJoin {
18
+ found: boolean;
19
+ /** The conventional `slices/<key>.md` path (relative to the `.em` file's own
20
+ * directory) this slice's doc binding resolves to — present even when
21
+ * `found` is false, so a consumer can point at exactly where a doc is
22
+ * expected. em export's `doc` field is frontmatter-only, never the
23
+ * rendered markdown body (see docs/cli.md) — em-portal reads and renders
24
+ * that body itself (src/em/docBody.ts) using this path, joined against
25
+ * the model file's own directory, the same sibling-file convention every
26
+ * other doc-aware em command uses. */
27
+ path: string;
28
+ reason: "no-doc-bound" | "binding-missing-file" | "frontmatter-invalid" | null;
29
+ status: string | null;
30
+ version: number | null;
31
+ implementedIn: string | null;
32
+ splitFrom: DocLineageRef[] | null;
33
+ mergedFrom: DocLineageRef[] | null;
34
+ supersededBy: DocLineageRef | DocLineageRef[] | null;
35
+ driftSignal: "in-sync" | "never-implemented" | "unpropagated-delta" | "implemented-without-link" | null;
36
+ ratifiedBy: string | null;
37
+ ratifiedOn: string | null;
38
+ /** `owner:`/`tracking:` frontmatter (added in schema 1.9, MIL-171) — who
39
+ * holds this slice (a person or team, free text) and a link into an
40
+ * external tracker mirroring it, respectively. Both hand-filled,
41
+ * `null` when absent — declared here even though em-portal was written
42
+ * against schema 1.8, per the "tolerate unknown fields" policy: a field
43
+ * we don't yet know about would simply be ignored, but these are worth
44
+ * surfacing once they exist. */
45
+ owner: string | null;
46
+ tracking: string | null;
47
+ }
48
+ export interface TypeRef {
49
+ name: string;
50
+ ref: string;
51
+ array: boolean;
52
+ }
53
+ export interface EmField {
54
+ name: string;
55
+ type: string | null;
56
+ typeRef: TypeRef | null;
57
+ tag: boolean;
58
+ renamedFrom: string[] | null;
59
+ assigned?: boolean;
60
+ }
61
+ export interface EmTag {
62
+ key: string;
63
+ kind: "identity" | "composite" | "external";
64
+ fields: string[] | null;
65
+ description: string | null;
66
+ }
67
+ export interface EmElement {
68
+ ref: string;
69
+ kind: string;
70
+ name: string;
71
+ line: number;
72
+ fields: EmField[] | null;
73
+ note: string | null;
74
+ issue: string | null;
75
+ divergence: string | null;
76
+ from: {
77
+ name: string;
78
+ ref: string;
79
+ }[] | null;
80
+ persona: string | null;
81
+ context: string | null;
82
+ again: boolean;
83
+ public: boolean;
84
+ tags: EmTag[] | null;
85
+ renamedFrom: string[] | null;
86
+ logicalRef: string | null;
87
+ }
88
+ export type SlicePattern = "state-change" | "state-view" | "automation" | "translation" | "unclassified";
89
+ export interface EmSlice {
90
+ key: string;
91
+ name: string;
92
+ index: number;
93
+ line: number;
94
+ source: string | null;
95
+ elements: EmElement[];
96
+ pattern: SlicePattern;
97
+ doc: SliceDocJoin;
98
+ }
99
+ export interface EmArrow {
100
+ from: string;
101
+ to: string;
102
+ fromRef: string | null;
103
+ toRef: string | null;
104
+ }
105
+ export interface EmNamedType {
106
+ ref: string;
107
+ name: string;
108
+ line: number;
109
+ fields: EmField[];
110
+ }
111
+ export interface EmModel {
112
+ name: string | null;
113
+ personas: string[];
114
+ contexts: string[];
115
+ hasAutomation: boolean;
116
+ types: EmNamedType[];
117
+ slices: EmSlice[];
118
+ arrows: EmArrow[];
119
+ }
120
+ export interface ExportDoc {
121
+ schemaVersion: string;
122
+ generator: EmGenerator;
123
+ source: {
124
+ path: string;
125
+ sha256: string;
126
+ };
127
+ model: EmModel;
128
+ diagnostics: EmDiagnostic[];
129
+ }
130
+ /** `schemaVersion` is `"<major>.<minor>"`. em-portal was written against the
131
+ * 1.x line (docs/cli.md, 1.8 as of this writing) — this only checks the
132
+ * major version, per em's own "renames/removals are a major bump" policy;
133
+ * a minor bump (new optional field) is exactly what "tolerate unknown
134
+ * fields" means and is never flagged. */
135
+ export declare function checkExportSchemaCompatible(doc: ExportDoc): string | null;
@@ -0,0 +1,22 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Types for `em export --json` (schemaVersion 1.8+, docs/cli.md in the em repo).
3
+ // Deliberately loose, not zod-validated: em's own versioning policy says
4
+ // "additive optional fields are a minor bump ... consumers must tolerate
5
+ // unknown fields" (docs/cli.md), and MIL-171 is landing owner:/tracking:
6
+ // fields on the doc join in parallel with this ticket. Plain `as` casts over
7
+ // `JSON.parse` already tolerate unknown fields for free — a strict schema
8
+ // validator (zod `.strict()`) would actively fight that contract, so this
9
+ // file only declares the fields em-portal actually reads, nothing more.
10
+ /** `schemaVersion` is `"<major>.<minor>"`. em-portal was written against the
11
+ * 1.x line (docs/cli.md, 1.8 as of this writing) — this only checks the
12
+ * major version, per em's own "renames/removals are a major bump" policy;
13
+ * a minor bump (new optional field) is exactly what "tolerate unknown
14
+ * fields" means and is never flagged. */
15
+ export function checkExportSchemaCompatible(doc) {
16
+ const major = doc.schemaVersion?.split(".")[0];
17
+ if (major !== "1") {
18
+ return `em export schemaVersion ${doc.schemaVersion} has a different major version than the 1.x line em-portal was built against — output may be incomplete or wrong.`;
19
+ }
20
+ return null;
21
+ }
22
+ //# sourceMappingURL=exportDoc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exportDoc.js","sourceRoot":"","sources":["../../src/em/exportDoc.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,iFAAiF;AACjF,yEAAyE;AACzE,yEAAyE;AACzE,yEAAyE;AACzE,6EAA6E;AAC7E,0EAA0E;AAC1E,0EAA0E;AAC1E,wEAAwE;AA2IxE;;;;0CAI0C;AAC1C,MAAM,UAAU,2BAA2B,CAAC,GAAc;IACxD,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;QAClB,OAAO,2BAA2B,GAAG,CAAC,aAAa,mHAAmH,CAAC;IACzK,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,41 @@
1
+ export interface EmRunResult {
2
+ stdout: string;
3
+ stderr: string;
4
+ }
5
+ export declare class EmCliError extends Error {
6
+ readonly args: string[];
7
+ readonly stdout: string;
8
+ readonly stderr: string;
9
+ readonly exitCode: number | null;
10
+ constructor(args: string[], stdout: string, stderr: string, exitCode: number | null);
11
+ }
12
+ /** Resolves the absolute path to the installed `em` CLI's entry point by reading
13
+ * the resolved `@milehimikey/em` package's own `package.json` (`bin.em`), rather
14
+ * than assuming a `node_modules/.bin/em` shim is on PATH — works the same way
15
+ * whether em-portal is run via `npm test`, a global install, or `npx`. */
16
+ export declare function resolveEmCliPath(fromDir?: string): string;
17
+ /** Runs `node <em-cli> ...args` and returns stdout/stderr. Throws EmCliError on
18
+ * a non-zero exit so callers don't have to remember to check `stderr`/exit
19
+ * code themselves — every em command that can refuse (a model with errors)
20
+ * does so via a non-zero exit, so this is the single choke point for that. */
21
+ export declare function runEm(args: string[], opts?: {
22
+ cwd?: string;
23
+ }): Promise<EmRunResult>;
24
+ /** `em export <file>` — the command's stdout IS JSON by default (no `--json`
25
+ * flag exists on `em export`; unlike most other em subcommands, JSON is its
26
+ * only output shape). Parsed, untyped-at-this-layer; the schema shape lives
27
+ * in exportDoc.ts, kept separate so run.ts stays a pure process boundary
28
+ * with no knowledge of em's document shapes. */
29
+ export declare function emExportJson(file: string): Promise<unknown>;
30
+ export interface EmStatusOptions {
31
+ testsDir?: string;
32
+ repo?: string;
33
+ }
34
+ /** `em status <files...> --json`. */
35
+ export declare function emStatusJson(files: string[], opts?: EmStatusOptions): Promise<unknown>;
36
+ /** `em render <file> -o <outPath>` — the full model diagram. */
37
+ export declare function emRenderModel(file: string, outPath: string): Promise<void>;
38
+ /** `em render <file> --slice <name> -o <outPath>` — one slice's own canonical
39
+ * pattern-shape diagram. `name` is the slice's display NAME (em render's
40
+ * `--slice` matches by exact name, not export key — see docs/cli.md). */
41
+ export declare function emRenderSlice(file: string, sliceName: string, outPath: string): Promise<void>;
package/dist/em/run.js ADDED
@@ -0,0 +1,99 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // The only place em-portal talks to `em`. em-portal is a separate package/repo
3
+ // from em (per docs/decisions/mil-162-teachable-navigator.md in the em repo) —
4
+ // it never imports em's internal model/pipeline modules, only its published
5
+ // CLI surface (`em export --json`, `em status --json`, `em render`). Every
6
+ // call here shells out to the exact `em` binary resolved from this package's
7
+ // own `@milehimikey/em` devDependency (a production install would add it as a
8
+ // real dependency instead — see README), so a portal build always uses
9
+ // whichever em version is actually installed, never a hardcoded path.
10
+ //
11
+ // This is also where MIL-162's unmeasured "process-spawn overhead" question
12
+ // gets an answer: build.ts times every call it makes through here (see
13
+ // BuildTiming) instead of assuming the in-process numbers the prototype
14
+ // measured still apply.
15
+ import { createRequire } from "node:module";
16
+ import { execFile } from "node:child_process";
17
+ import { promisify } from "node:util";
18
+ import { dirname, join } from "node:path";
19
+ const execFileAsync = promisify(execFile);
20
+ export class EmCliError extends Error {
21
+ args;
22
+ stdout;
23
+ stderr;
24
+ exitCode;
25
+ constructor(args, stdout, stderr, exitCode) {
26
+ super(`em ${args.join(" ")} failed (exit ${exitCode}): ${stderr.trim() || stdout.trim()}`);
27
+ this.args = args;
28
+ this.stdout = stdout;
29
+ this.stderr = stderr;
30
+ this.exitCode = exitCode;
31
+ this.name = "EmCliError";
32
+ }
33
+ }
34
+ let cachedCliPath = null;
35
+ /** Resolves the absolute path to the installed `em` CLI's entry point by reading
36
+ * the resolved `@milehimikey/em` package's own `package.json` (`bin.em`), rather
37
+ * than assuming a `node_modules/.bin/em` shim is on PATH — works the same way
38
+ * whether em-portal is run via `npm test`, a global install, or `npx`. */
39
+ export function resolveEmCliPath(fromDir = process.cwd()) {
40
+ if (cachedCliPath)
41
+ return cachedCliPath;
42
+ const require = createRequire(join(fromDir, "package.json"));
43
+ const pkgJsonPath = require.resolve("@milehimikey/em/package.json");
44
+ const pkg = require("@milehimikey/em/package.json");
45
+ const binRel = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.em;
46
+ if (!binRel) {
47
+ throw new Error(`@milehimikey/em's package.json (${pkgJsonPath}) declares no "em" bin entry`);
48
+ }
49
+ cachedCliPath = join(dirname(pkgJsonPath), binRel);
50
+ return cachedCliPath;
51
+ }
52
+ /** Runs `node <em-cli> ...args` and returns stdout/stderr. Throws EmCliError on
53
+ * a non-zero exit so callers don't have to remember to check `stderr`/exit
54
+ * code themselves — every em command that can refuse (a model with errors)
55
+ * does so via a non-zero exit, so this is the single choke point for that. */
56
+ export async function runEm(args, opts = {}) {
57
+ const cliPath = resolveEmCliPath(opts.cwd ?? process.cwd());
58
+ try {
59
+ const { stdout, stderr } = await execFileAsync(process.execPath, [cliPath, ...args], {
60
+ cwd: opts.cwd,
61
+ maxBuffer: 64 * 1024 * 1024,
62
+ });
63
+ return { stdout, stderr };
64
+ }
65
+ catch (err) {
66
+ const e = err;
67
+ throw new EmCliError(args, e.stdout ?? "", e.stderr ?? "", e.code ?? null);
68
+ }
69
+ }
70
+ /** `em export <file>` — the command's stdout IS JSON by default (no `--json`
71
+ * flag exists on `em export`; unlike most other em subcommands, JSON is its
72
+ * only output shape). Parsed, untyped-at-this-layer; the schema shape lives
73
+ * in exportDoc.ts, kept separate so run.ts stays a pure process boundary
74
+ * with no knowledge of em's document shapes. */
75
+ export async function emExportJson(file) {
76
+ const { stdout } = await runEm(["export", file]);
77
+ return JSON.parse(stdout);
78
+ }
79
+ /** `em status <files...> --json`. */
80
+ export async function emStatusJson(files, opts = {}) {
81
+ const args = ["status", ...files, "--json"];
82
+ if (opts.testsDir)
83
+ args.push("--tests", opts.testsDir);
84
+ if (opts.repo)
85
+ args.push("--repo", opts.repo);
86
+ const { stdout } = await runEm(args);
87
+ return JSON.parse(stdout);
88
+ }
89
+ /** `em render <file> -o <outPath>` — the full model diagram. */
90
+ export async function emRenderModel(file, outPath) {
91
+ await runEm(["render", file, "-o", outPath]);
92
+ }
93
+ /** `em render <file> --slice <name> -o <outPath>` — one slice's own canonical
94
+ * pattern-shape diagram. `name` is the slice's display NAME (em render's
95
+ * `--slice` matches by exact name, not export key — see docs/cli.md). */
96
+ export async function emRenderSlice(file, sliceName, outPath) {
97
+ await runEm(["render", file, "--slice", sliceName, "-o", outPath]);
98
+ }
99
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/em/run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ export interface StatusConformanceEntry {
2
+ file: string;
3
+ modelDir: string;
4
+ hasStateFile: boolean;
5
+ lastConformance: {
6
+ date: string;
7
+ revision: string;
8
+ } | null;
9
+ repo: string;
10
+ commitsBehindHead: number | null;
11
+ slicePRsBehindHead: number | null;
12
+ error: string | null;
13
+ }
14
+ export interface StatusInvariants {
15
+ testsDir: string;
16
+ total: number;
17
+ cited: number;
18
+ uncovered: number;
19
+ }
20
+ export interface StatusDoc {
21
+ statusSchemaVersion: string;
22
+ generator: {
23
+ name: string;
24
+ version: string;
25
+ };
26
+ files: string[];
27
+ slices: {
28
+ total: number;
29
+ byStatus: {
30
+ draft: number;
31
+ reviewed: number;
32
+ readyToImplement: number;
33
+ implemented: number;
34
+ noDoc: number;
35
+ frontmatterInvalid: number;
36
+ unknown: number;
37
+ };
38
+ };
39
+ driftSignal: {
40
+ inSync: number;
41
+ neverImplemented: number;
42
+ unpropagatedDelta: number;
43
+ implementedWithoutLink: number;
44
+ notApplicable: number;
45
+ frontmatterInvalid: number;
46
+ };
47
+ invariants: StatusInvariants | null;
48
+ issues: {
49
+ openIssues: number;
50
+ openQuestionsTotal: number;
51
+ openQuestionsUnchecked: number;
52
+ };
53
+ conformance: StatusConformanceEntry[];
54
+ diagnostics: unknown[];
55
+ }
56
+ export declare function checkStatusSchemaCompatible(doc: StatusDoc): string | null;
@@ -0,0 +1,11 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Types for `em status --json` (statusSchemaVersion 1.1+, docs/cli.md in the
3
+ // em repo). Same tolerant-of-unknown-fields posture as exportDoc.ts.
4
+ export function checkStatusSchemaCompatible(doc) {
5
+ const major = doc.statusSchemaVersion?.split(".")[0];
6
+ if (major !== "1") {
7
+ return `em status statusSchemaVersion ${doc.statusSchemaVersion} has a different major version than the 1.x line em-portal was built against — output may be incomplete or wrong.`;
8
+ }
9
+ return null;
10
+ }
11
+ //# sourceMappingURL=statusDoc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statusDoc.js","sourceRoot":"","sources":["../../src/em/statusDoc.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,6EAA6E;AAC7E,qEAAqE;AAsDrE,MAAM,UAAU,2BAA2B,CAAC,GAAc;IACxD,MAAM,KAAK,GAAG,GAAG,CAAC,mBAAmB,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;QAClB,OAAO,iCAAiC,GAAG,CAAC,mBAAmB,mHAAmH,CAAC;IACrL,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,11 @@
1
+ export declare function escapeHtml(s: string): string;
2
+ export interface Crumb {
3
+ label: string;
4
+ href: string;
5
+ }
6
+ /** `homeHref` is always relative to the page being built so the whole site
7
+ * works opened straight off disk via `file://` or served from any subpath —
8
+ * no absolute `href="/"` anywhere (that would jump to the filesystem/host
9
+ * root instead of the site root when the portal is published under a
10
+ * subpath, e.g. GitHub Pages project sites). */
11
+ export declare function layout(title: string, bodyHtml: string, homeHref: string, crumbs?: Crumb[]): string;
@@ -0,0 +1,84 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Shared HTML-string helpers for every page builder. Hand-written template
3
+ // literals, no templating library or client-side framework — every page is
4
+ // plain, self-contained, static HTML (no external CDN, no build-time
5
+ // dependency the reader's browser needs to fetch), per the fully-static
6
+ // constraint in docs/decisions/mil-162-teachable-navigator.md.
7
+ export function escapeHtml(s) {
8
+ return s
9
+ .replace(/&/g, "&amp;")
10
+ .replace(/</g, "&lt;")
11
+ .replace(/>/g, "&gt;")
12
+ .replace(/"/g, "&quot;")
13
+ .replace(/'/g, "&#39;");
14
+ }
15
+ const STYLE = `
16
+ :root { color-scheme: light dark; }
17
+ * { box-sizing: border-box; }
18
+ body {
19
+ margin: 0; font-family: system-ui, -apple-system, "Segoe UI", sans-serif; line-height: 1.5;
20
+ background: #fafafa; color: #1f2933;
21
+ }
22
+ header {
23
+ padding: .75rem 1.25rem; background: #1f2933; color: #fff;
24
+ display: flex; align-items: baseline; gap: .75rem; flex-wrap: wrap;
25
+ }
26
+ header a { color: #fff; text-decoration: none; font-weight: 600; }
27
+ header .crumbs { color: #cbd2d9; font-size: 13px; }
28
+ header .crumbs a { color: #cbd2d9; font-weight: 400; text-decoration: underline; }
29
+ main { max-width: 1000px; margin: 0 auto; padding: 1.5rem 1.25rem 3rem; }
30
+ table { border-collapse: collapse; width: 100%; margin: 1rem 0; }
31
+ th, td { text-align: left; padding: .4rem .6rem; border-bottom: 1px solid #ddd; font-size: 14px; vertical-align: top; }
32
+ th { font-weight: 600; background: #f0f2f5; }
33
+ .badge { display: inline-block; padding: .1rem .5rem; border-radius: 999px; font-size: 12px; background: #e4e7eb; }
34
+ .badge.ok { background: #d3f3df; color: #10633c; }
35
+ .badge.warn { background: #fdecc8; color: #8a5a00; }
36
+ .badge.bad { background: #fbdbd6; color: #9c2b1d; }
37
+ .pattern { font-size: 12px; color: #52606d; }
38
+ .stat-row { display: flex; flex-wrap: wrap; gap: .75rem; margin: 1rem 0 1.5rem; }
39
+ .stat { background: #fff; border: 1px solid #ddd; border-radius: 8px; padding: .6rem .9rem; min-width: 9rem; }
40
+ .stat .n { font-size: 1.4rem; font-weight: 700; display: block; }
41
+ .stat .label { font-size: 12px; color: #52606d; }
42
+ .diagram-frame {
43
+ height: 420px; border: 1px solid #ddd; margin: 1rem 0; overflow: hidden; background: #fff;
44
+ display: flex; align-items: flex-start; justify-content: center;
45
+ }
46
+ object.diagram { width: auto; height: 100%; max-width: 100%; }
47
+ .full-diagram-link { margin: 0 0 1rem; font-size: 13px; }
48
+ .doc { margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid #ddd; }
49
+ .doc h1 { font-size: 1.3rem; }
50
+ .doc h2 { font-size: 1.05rem; }
51
+ .no-doc { color: #7b8794; font-style: italic; }
52
+ .permalink { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; color: #52606d; }
53
+ .permalink a { color: inherit; }
54
+ code { background: #eee; padding: .05rem .3rem; border-radius: 3px; }
55
+ footer { max-width: 1000px; margin: 0 auto; padding: 0 1.25rem 2rem; font-size: 12px; color: #7b8794; }
56
+ `;
57
+ /** `homeHref` is always relative to the page being built so the whole site
58
+ * works opened straight off disk via `file://` or served from any subpath —
59
+ * no absolute `href="/"` anywhere (that would jump to the filesystem/host
60
+ * root instead of the site root when the portal is published under a
61
+ * subpath, e.g. GitHub Pages project sites). */
62
+ export function layout(title, bodyHtml, homeHref, crumbs = []) {
63
+ const crumbHtml = crumbs.length
64
+ ? `<span class="crumbs">${crumbs.map((c) => `&rsaquo; <a href="${escapeHtml(c.href)}">${escapeHtml(c.label)}</a>`).join(" ")}</span>`
65
+ : "";
66
+ return `<!doctype html>
67
+ <html lang="en">
68
+ <head>
69
+ <meta charset="utf-8" />
70
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
71
+ <title>${escapeHtml(title)}</title>
72
+ <style>${STYLE}</style>
73
+ </head>
74
+ <body>
75
+ <header><a href="${escapeHtml(homeHref)}">em portal</a>${crumbHtml}</header>
76
+ <main>
77
+ ${bodyHtml}
78
+ </main>
79
+ <footer>Generated by em-portal — a static, read-only view over <code>em export</code>/<code>em status</code>. No server, no storage of its own; git is the source of truth.</footer>
80
+ </body>
81
+ </html>
82
+ `;
83
+ }
84
+ //# sourceMappingURL=html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/pages/html.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,2EAA2E;AAC3E,2EAA2E;AAC3E,qEAAqE;AACrE,wEAAwE;AACxE,+DAA+D;AAE/D,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,OAAO,CAAC;SACL,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyCb,CAAC;AAOF;;;;iDAIiD;AACjD,MAAM,UAAU,MAAM,CAAC,KAAa,EAAE,QAAgB,EAAE,QAAgB,EAAE,SAAkB,EAAE;IAC5F,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM;QAC7B,CAAC,CAAC,wBAAwB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,qBAAqB,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS;QACrI,CAAC,CAAC,EAAE,CAAC;IACP,OAAO;;;;;WAKE,UAAU,CAAC,KAAK,CAAC;WACjB,KAAK;;;qBAGK,UAAU,CAAC,QAAQ,CAAC,kBAAkB,SAAS;;EAElE,QAAQ;;;;;CAKT,CAAC;AACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { StatusDoc } from "../em/statusDoc.js";
2
+ import { PortalSummary } from "./types.js";
3
+ export declare function renderIndexPage(status: StatusDoc, summary: PortalSummary): string;
@@ -0,0 +1,128 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // The portal's landing page: `em status`'s rollup up front (MIL-162 property
3
+ // #2, "state up front" — a reader's first question is "is this healthy," not
4
+ // a slice list), then the multi-model index (property #3) and any resolved
5
+ // cross-model links. Drill-down is model -> slice -> doc, in that order — this
6
+ // page never lists individual slices itself.
7
+ import { escapeHtml, layout } from "./html.js";
8
+ import { elementDeepLink } from "../refs.js";
9
+ function pct(numer, denom) {
10
+ if (denom === 0)
11
+ return "n/a";
12
+ return `${((numer / denom) * 100).toFixed(0)}%`;
13
+ }
14
+ function conformanceBadge(entry) {
15
+ if (entry.error)
16
+ return `<span class="badge warn">unverified</span>`;
17
+ if (!entry.hasStateFile)
18
+ return `<span class="badge">no conform history</span>`;
19
+ const behind = (entry.commitsBehindHead ?? 0) + (entry.slicePRsBehindHead ?? 0);
20
+ if (behind > 0)
21
+ return `<span class="badge warn">${behind} behind HEAD</span>`;
22
+ return `<span class="badge ok">up to date</span>`;
23
+ }
24
+ function renderStatusSection(status) {
25
+ const total = status.slices.total;
26
+ const s = status.slices.byStatus;
27
+ const documented = total - s.noDoc;
28
+ const d = status.driftSignal;
29
+ const stats = [
30
+ { n: `${s.implemented}/${total}`, label: "implemented" },
31
+ { n: `${documented}/${total}`, label: `documented (${pct(documented, total)})` },
32
+ { n: `${status.issues.openIssues}`, label: "open issues" },
33
+ {
34
+ n: status.invariants ? `${status.invariants.cited}/${status.invariants.total}` : "n/a",
35
+ label: "invariants covered",
36
+ },
37
+ ];
38
+ const statHtml = stats
39
+ .map((st) => `<div class="stat"><span class="n">${escapeHtml(st.n)}</span><span class="label">${escapeHtml(st.label)}</span></div>`)
40
+ .join("\n");
41
+ const conformanceRows = status.conformance
42
+ .map((c) => `<tr>
43
+ <td><code>${escapeHtml(c.file)}</code></td>
44
+ <td>${c.hasStateFile && c.lastConformance ? escapeHtml(`${c.lastConformance.revision} (${c.lastConformance.date})`) : "&mdash;"}</td>
45
+ <td>${c.commitsBehindHead ?? "&mdash;"}</td>
46
+ <td>${c.slicePRsBehindHead ?? "&mdash;"}</td>
47
+ <td>${conformanceBadge(c)}</td>
48
+ </tr>`)
49
+ .join("\n");
50
+ return ` <h1>State of the system</h1>
51
+ <div class="stat-row">
52
+ ${statHtml}
53
+ </div>
54
+ <h2>Slices by status</h2>
55
+ <p>
56
+ <span class="badge">${s.draft} draft</span>
57
+ <span class="badge">${s.reviewed} reviewed</span>
58
+ <span class="badge">${s.readyToImplement} ready-to-implement</span>
59
+ <span class="badge ok">${s.implemented} implemented</span>
60
+ <span class="badge">${s.noDoc} no doc</span>
61
+ ${s.frontmatterInvalid ? `<span class="badge bad">${s.frontmatterInvalid} frontmatter invalid</span>` : ""}
62
+ ${s.unknown ? `<span class="badge">${s.unknown} unknown status</span>` : ""}
63
+ </p>
64
+ <h2>Drift signal</h2>
65
+ <p>
66
+ <span class="badge ok">${d.inSync} in-sync</span>
67
+ <span class="badge warn">${d.neverImplemented} never-implemented</span>
68
+ <span class="badge bad">${d.unpropagatedDelta} unpropagated-delta</span>
69
+ <span class="badge bad">${d.implementedWithoutLink} implemented-without-link</span>
70
+ <span class="badge">${d.notApplicable} n/a (no doc)</span>
71
+ </p>
72
+ <h2>Freshness (conformance)</h2>
73
+ <table>
74
+ <thead><tr><th>Model</th><th>Last conformed</th><th>Commits behind</th><th>Slice-PRs behind</th><th></th></tr></thead>
75
+ <tbody>
76
+ ${conformanceRows}
77
+ </tbody>
78
+ </table>`;
79
+ }
80
+ export function renderIndexPage(status, summary) {
81
+ const modelRows = summary.models
82
+ .map((m) => ` <tr>
83
+ <td><a href="${escapeHtml(m.key)}/index.html">${escapeHtml(m.name)}</a></td>
84
+ <td>${m.sliceCount}</td>
85
+ <td><code>${escapeHtml(m.file)}</code></td>
86
+ </tr>`)
87
+ .join("\n");
88
+ const linkRows = summary.crossModelLinks
89
+ .map((l) => {
90
+ // MIL-173: both ends of a cross-model link resolve straight to the
91
+ // specific publishing/referencing ELEMENT via the same deep-link
92
+ // scheme a slice page's own permalinks use — not just the model's
93
+ // landing page — since the whole point of surfacing this row is "here
94
+ // is exactly which element on each side."
95
+ const fromLink = elementDeepLink(l.fromModelKey, l.fromRef);
96
+ const toLink = elementDeepLink(l.toModelKey, l.toElementRef);
97
+ return ` <tr>
98
+ <td><code>${escapeHtml(l.eventName)}</code></td>
99
+ <td><a href="${escapeHtml(fromLink)}">${escapeHtml(l.fromModelKey)}</a></td>
100
+ <td><a href="${escapeHtml(toLink)}">${escapeHtml(l.toModelKey)}</a></td>
101
+ </tr>`;
102
+ })
103
+ .join("\n");
104
+ const crossModelSection = summary.crossModelLinks.length > 0
105
+ ? ` <h2>Cross-model links</h2>
106
+ <p>Resolved by matching a <code>public</code> event's name against every other model's
107
+ element names — a naming-convention join, not a compiler-verified reference (see
108
+ the em-portal README's "Cross-model navigation" section). Each link goes straight to the
109
+ specific element on either side, not just that model's landing page.</p>
110
+ <table>
111
+ <thead><tr><th>Public event</th><th>Published by</th><th>Referenced by</th></tr></thead>
112
+ <tbody>
113
+ ${linkRows}
114
+ </tbody>
115
+ </table>`
116
+ : "";
117
+ const body = `${renderStatusSection(status)}
118
+ <h2>Models</h2>
119
+ <table>
120
+ <thead><tr><th>Model</th><th>Slices</th><th>Source</th></tr></thead>
121
+ <tbody>
122
+ ${modelRows}
123
+ </tbody>
124
+ </table>
125
+ ${crossModelSection}`;
126
+ return layout(summary.title, body, "index.html");
127
+ }
128
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pages/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { SlicePattern } from "../em/exportDoc.js";
2
+ export interface ModelPageSliceRow {
3
+ key: string;
4
+ name: string;
5
+ pattern: SlicePattern;
6
+ hasDoc: boolean;
7
+ status: string | null;
8
+ driftSignal: string | null;
9
+ }
10
+ export interface ModelPageArgs {
11
+ modelKey: string;
12
+ modelName: string;
13
+ file: string;
14
+ diagramFile: string;
15
+ slices: ModelPageSliceRow[];
16
+ }
17
+ export declare function renderModelPage(args: ModelPageArgs): string;