@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.
- package/LICENSE +21 -0
- package/README.md +231 -0
- package/dist/build.d.ts +21 -0
- package/dist/build.js +132 -0
- package/dist/build.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +43 -0
- package/dist/cli.js.map +1 -0
- package/dist/crossModel.d.ts +14 -0
- package/dist/crossModel.js +49 -0
- package/dist/crossModel.js.map +1 -0
- package/dist/em/docBody.d.ts +5 -0
- package/dist/em/docBody.js +43 -0
- package/dist/em/docBody.js.map +1 -0
- package/dist/em/exportDoc.d.ts +135 -0
- package/dist/em/exportDoc.js +22 -0
- package/dist/em/exportDoc.js.map +1 -0
- package/dist/em/run.d.ts +41 -0
- package/dist/em/run.js +99 -0
- package/dist/em/run.js.map +1 -0
- package/dist/em/statusDoc.d.ts +56 -0
- package/dist/em/statusDoc.js +11 -0
- package/dist/em/statusDoc.js.map +1 -0
- package/dist/pages/html.d.ts +11 -0
- package/dist/pages/html.js +84 -0
- package/dist/pages/html.js.map +1 -0
- package/dist/pages/index.d.ts +3 -0
- package/dist/pages/index.js +128 -0
- package/dist/pages/index.js.map +1 -0
- package/dist/pages/model.d.ts +17 -0
- package/dist/pages/model.js +54 -0
- package/dist/pages/model.js.map +1 -0
- package/dist/pages/slice.d.ts +16 -0
- package/dist/pages/slice.js +144 -0
- package/dist/pages/slice.js.map +1 -0
- package/dist/pages/types.d.ts +22 -0
- package/dist/pages/types.js +4 -0
- package/dist/pages/types.js.map +1 -0
- package/dist/refs.d.ts +36 -0
- package/dist/refs.js +69 -0
- package/dist/refs.js.map +1 -0
- package/dist/slug.d.ts +5 -0
- package/dist/slug.js +30 -0
- package/dist/slug.js.map +1 -0
- 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"}
|
package/dist/em/run.d.ts
ADDED
|
@@ -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, "&")
|
|
10
|
+
.replace(/</g, "<")
|
|
11
|
+
.replace(/>/g, ">")
|
|
12
|
+
.replace(/"/g, """)
|
|
13
|
+
.replace(/'/g, "'");
|
|
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) => `› <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,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})`) : "—"}</td>
|
|
45
|
+
<td>${c.commitsBehindHead ?? "—"}</td>
|
|
46
|
+
<td>${c.slicePRsBehindHead ?? "—"}</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;
|