@miragon/design-iq-validator 5.5.0 → 5.7.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/README.md +44 -7
- package/dist/cli.js +1 -1
- package/dist/{content-wjC_epKd.js → content-B0u0kwZN.js} +1 -1
- package/dist/{content-Bsv9ZLFd.js → content-DfRb_tAL.js} +18 -13
- package/dist/{validate-DjtZJTGk.js → validate-CUTctX4d.js} +162 -25
- package/dist/validate.js +1 -1
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -3,13 +3,50 @@
|
|
|
3
3
|
Deterministic validator for designIQ content repositories: `designiq.yml` discovery, BPMN
|
|
4
4
|
structural checks (flow soundness, complete BPMNDI coverage), DMN structure and DMNDI coverage,
|
|
5
5
|
the generic cross-model reference rule (every required reference — `callActivity` calls,
|
|
6
|
-
`businessRuleTask` decides — must resolve in the repo),
|
|
7
|
-
every other registered notation (a
|
|
8
|
-
lenient by design; a `mermaid` fence in a
|
|
9
|
-
|
|
10
|
-
document's line). Every finding
|
|
11
|
-
`refs/dangling`). It treats the target
|
|
12
|
-
Exit code 0 = no errors (warnings
|
|
6
|
+
`businessRuleTask` decides — must resolve in the repo), the schema check of the Miragon JSON
|
|
7
|
+
notations, and a per-mediaKind parse baseline for every other registered notation (a file that
|
|
8
|
+
is no JSON or XML is an ERROR; DSL notations are lenient by design; a `mermaid` fence in a
|
|
9
|
+
Markdown document that does not start with a diagram type such as `flowchart` or
|
|
10
|
+
`sequenceDiagram` is a WARN, `mermaid/diagram-type`, naming the document's line). Every finding
|
|
11
|
+
carries a stable `ruleId` (e.g. `bpmn/flow`, `dmn/di`, `refs/dangling`). It treats the target
|
|
12
|
+
repo as pure data — it never executes content-repo code. Exit code 0 = no errors (warnings
|
|
13
|
+
allowed), 1 = errors.
|
|
14
|
+
|
|
15
|
+
## Team Topologies, Context Maps and Value Chains
|
|
16
|
+
|
|
17
|
+
A `.tt`/`.ttm.json`, `.cm.json` or `.vc.json` file is parsed with the schema model its canvas
|
|
18
|
+
opens it with (`@miragon/team-topologies-schema-model`, `@miragon/context-maps-schema-model`,
|
|
19
|
+
`@miragon/value-chain-schema-model`). Whatever the schema model refuses, the canvas cannot open,
|
|
20
|
+
so each refusal is an ERROR (`team-topology/schema`, `context-map/schema`,
|
|
21
|
+
`value-chain/schema`) that names the offending value by its JSON path, e.g.
|
|
22
|
+
`nodes.1.id: duplicate id "a"`. Refused among others:
|
|
23
|
+
|
|
24
|
+
- a duplicate id, and the ids the diagram keeps for itself: `tt-root`, `cm-root`, `vc-root`,
|
|
25
|
+
every id starting with `__implicitroot`, and `assoc_<annotation id>` in a Team Topology;
|
|
26
|
+
- in all three notations, an id that names a built-in JavaScript object property such as
|
|
27
|
+
`constructor`, `toString` or `__proto__`: the canvas keeps its elements in a plain object and
|
|
28
|
+
cannot import it;
|
|
29
|
+
- in a Team Topology whose `version` is not 1, a file without `version` included: an interaction
|
|
30
|
+
without `position` or `size`, at the missing field (`interactions.0.size`). Opened anyway, it
|
|
31
|
+
would drop out of the file with the next save. A version 1 file still loses its edge
|
|
32
|
+
interactions, which have no geometry;
|
|
33
|
+
- in a Context Map: a JSON list instead of an object, a `version` newer than 1, coordinates or
|
|
34
|
+
sizes beyond 1,000,000, and a repeated id at the path of the repeat, e.g.
|
|
35
|
+
`contexts.2.id: duplicate id "a"; ids must be unique across contexts and relationships`;
|
|
36
|
+
- in a Value Chain: keys other than `x`, `y`, `width`, `height` in `bounds` and other than `x`,
|
|
37
|
+
`y` in a waypoint, coordinates beyond ±1e9, a `__proto__` key anywhere, and nesting deeper than
|
|
38
|
+
100 levels;
|
|
39
|
+
- in a Team Topology and a Context Map: a document nested too deep for the schema model's
|
|
40
|
+
serializer, which every canvas save runs, as `(root): could not be checked: …`.
|
|
41
|
+
|
|
42
|
+
A Context Map's strategic-DDD rules (`validateDocument`, e.g. `protect-or-conform`,
|
|
43
|
+
`endpoints-on-map`) are WARNs, `context-map/semantics`, with the rule named at the end of the
|
|
44
|
+
message: the canvas opens such a map and marks the finding.
|
|
45
|
+
|
|
46
|
+
Paths count in the file, a Team Topology's interactions included. Some rules only run once the
|
|
47
|
+
document's shape is valid: the id rules of a Team Topology and a Value Chain, and a Context Map's
|
|
48
|
+
strategic-DDD rules. Fixing the reported errors can therefore bring up further ones, so check
|
|
49
|
+
again until the file is clean.
|
|
13
50
|
|
|
14
51
|
## Usage
|
|
15
52
|
|
package/dist/cli.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { t as CONTENT_CONFIG_FILE, i as contentConfigConflict, a as discoverModels, o as legacyContentConfigFile, s as loadContentConfig } from "./content-
|
|
1
|
+
export { t as CONTENT_CONFIG_FILE, i as contentConfigConflict, a as discoverModels, o as legacyContentConfigFile, s as loadContentConfig } from "./content-DfRb_tAL.js";
|
|
@@ -535,22 +535,27 @@ function extractWardley(raw) {
|
|
|
535
535
|
edges
|
|
536
536
|
};
|
|
537
537
|
}
|
|
538
|
+
const TT_EDGE_INTERACTIONS_VERSION = 1;
|
|
538
539
|
function extractTeamTopology(raw) {
|
|
539
540
|
const data = JSON.parse(raw);
|
|
541
|
+
const list = (v) => Array.isArray(v) ? v : [];
|
|
542
|
+
const teams = list(data.nodes).map((n) => ({
|
|
543
|
+
id: n.id,
|
|
544
|
+
type: n.type ?? "team",
|
|
545
|
+
name: n.label ?? n.name,
|
|
546
|
+
extra: n.description ? { description: n.description } : void 0
|
|
547
|
+
}));
|
|
548
|
+
const placed = (i) => "position" in i && "size" in i;
|
|
549
|
+
const interactions = list(data.interactions).filter((i) => i !== null && typeof i === "object").filter((i) => data.version !== TT_EDGE_INTERACTIONS_VERSION || placed(i)).map((i) => ({
|
|
550
|
+
id: i.id,
|
|
551
|
+
type: "interaction",
|
|
552
|
+
name: i.label,
|
|
553
|
+
extra: { mode: i.mode }
|
|
554
|
+
}));
|
|
540
555
|
return {
|
|
541
556
|
notation: "team-topology",
|
|
542
|
-
nodes:
|
|
543
|
-
|
|
544
|
-
type: n.type ?? "team",
|
|
545
|
-
name: n.label ?? n.name,
|
|
546
|
-
extra: n.description ? { description: n.description } : void 0
|
|
547
|
-
})),
|
|
548
|
-
edges: (data.edges ?? []).map((e, i) => ({
|
|
549
|
-
id: e.id ?? `edge-${i}`,
|
|
550
|
-
from: e.source ?? e.from,
|
|
551
|
-
to: e.target ?? e.to,
|
|
552
|
-
kind: e.interaction ?? e.type ?? "interaction"
|
|
553
|
-
}))
|
|
557
|
+
nodes: [...teams, ...interactions],
|
|
558
|
+
edges: []
|
|
554
559
|
};
|
|
555
560
|
}
|
|
556
561
|
const STORM_ID_PREFIX = {
|
|
@@ -801,7 +806,7 @@ function extractValueChain(raw) {
|
|
|
801
806
|
nodes: (data.elements ?? []).map((el) => ({
|
|
802
807
|
id: el.id,
|
|
803
808
|
type: el.elementType ?? "element",
|
|
804
|
-
name: el.
|
|
809
|
+
name: el.name
|
|
805
810
|
})),
|
|
806
811
|
edges: (data.connections ?? []).map((c, i) => ({
|
|
807
812
|
id: c.id ?? `conn-${i}`,
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { _ as byId, c as resolveContentConfigFile, d as extractModelGraph, f as parseXml, g as byExtension, h as kindOf, l as XMLValidator, m as requirementHref, p as readBpmn, r as CONTENT_CONFIG_NAMES, u as asArray } from "./content-
|
|
1
|
+
import { _ as byId, c as resolveContentConfigFile, d as extractModelGraph, f as parseXml, g as byExtension, h as kindOf, l as XMLValidator, m as requirementHref, p as readBpmn, r as CONTENT_CONFIG_NAMES, u as asArray } from "./content-DfRb_tAL.js";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { relative, resolve } from "node:path";
|
|
4
|
+
import { parseDocumentText, serializeDocumentText, validateDocument } from "@miragon/context-maps-schema-model";
|
|
5
|
+
import { parseDocument, serializeDocument } from "@miragon/team-topologies-schema-model";
|
|
6
|
+
import { documentIssues } from "@miragon/value-chain-schema-model";
|
|
4
7
|
//#region ../notations/refs.ts
|
|
5
8
|
const REF_EMITTERS = { bpmn: (graph) => {
|
|
6
9
|
const refs = [];
|
|
@@ -476,11 +479,23 @@ const MERMAID_DIAGRAM_TYPES = [
|
|
|
476
479
|
"railroad-beta",
|
|
477
480
|
"railroad-ebnf-beta",
|
|
478
481
|
"railroad-abnf-beta",
|
|
479
|
-
"railroad-peg-beta"
|
|
482
|
+
"railroad-peg-beta",
|
|
483
|
+
"usecase-beta",
|
|
484
|
+
"agentflow-beta"
|
|
480
485
|
];
|
|
481
486
|
const FRONT_MATTER = /^([^\S\n\r]*)-{3}\s*[\n\r](.*?)[\n\r]\1-{3}\s*[\n\r]+/s;
|
|
482
487
|
const DIRECTIVE = /%{2}{\s*(?:(\w+)\s*:|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi;
|
|
483
488
|
const COMMENT_LINE = /^\s*%%(?!{)[^\n]+\n?/gm;
|
|
489
|
+
/**
|
|
490
|
+
* The diagram types whose parser reads the comment lines too: their db sets
|
|
491
|
+
* preserveCommentsWhenParsing (src/Diagram.ts), so their errors count those
|
|
492
|
+
* lines. mermaid's detectors for them (src/diagrams/eventmodeling/detector.ts,
|
|
493
|
+
* src/diagrams/agentflow/afDetector.ts), against the header line.
|
|
494
|
+
*/
|
|
495
|
+
const KEEPS_COMMENTS = /^(?:eventmodeling|agentflow-beta\b)/;
|
|
496
|
+
/** mermaid's default maxTextSize, which apps/web keeps (a secure key): past
|
|
497
|
+
* it, mermaid parses even those types without their comment lines */
|
|
498
|
+
const MAX_TEXT_SIZE = 5e4;
|
|
484
499
|
/** a header keyword, followed by the end of the line, a space, ":" or ";" */
|
|
485
500
|
const HEADER = new RegExp(`^(?:${MERMAID_DIAGRAM_TYPES.map((t) => t.replace(/[.*+?^${}()|[\]\\-]/g, "\\$&")).join("|")})(?=$|[\\s:;])`);
|
|
486
501
|
/** mermaid's cleanupText: line breaks to \n, tag attributes single-quoted;
|
|
@@ -488,11 +503,34 @@ const HEADER = new RegExp(`^(?:${MERMAID_DIAGRAM_TYPES.map((t) => t.replace(/[.*
|
|
|
488
503
|
function cleanup(source) {
|
|
489
504
|
return source.replace(/\r\n?/g, "\n").replace(/<(\w+)([^>]*)>/g, (_tag, name, attrs) => `<${name}${attrs.replace(/="([^"]*)"/g, "='$1'")}>`);
|
|
490
505
|
}
|
|
506
|
+
/** the first line that is neither blank nor a bare `%%` comment (mermaid's
|
|
507
|
+
* type detection skips those too), -1 when there is none */
|
|
508
|
+
function headerIndex(lines) {
|
|
509
|
+
return lines.findIndex((l) => l.trim() !== "" && !l.trim().startsWith("%%"));
|
|
510
|
+
}
|
|
511
|
+
/** mermaid's preprocess (src/preprocess.ts), step by step */
|
|
512
|
+
function preprocess(source) {
|
|
513
|
+
const text = cleanup(source);
|
|
514
|
+
const frontMatter = FRONT_MATTER.exec(text)?.[0] ?? "";
|
|
515
|
+
const body = text.slice(frontMatter.length);
|
|
516
|
+
const withComments = body.replace(DIRECTIVE, "");
|
|
517
|
+
const cleaned = withComments.replace(COMMENT_LINE, "").trimStart();
|
|
518
|
+
const cleanedLines = cleaned.split("\n");
|
|
519
|
+
const header = cleanedLines[headerIndex(cleanedLines)]?.trim() ?? "";
|
|
520
|
+
const keepsComments = KEEPS_COMMENTS.test(header) && withComments.length <= MAX_TEXT_SIZE;
|
|
521
|
+
return {
|
|
522
|
+
text,
|
|
523
|
+
frontMatterLines: frontMatter.split("\n").length - 1,
|
|
524
|
+
body,
|
|
525
|
+
keepsComments,
|
|
526
|
+
code: keepsComments ? withComments : cleaned
|
|
527
|
+
};
|
|
528
|
+
}
|
|
491
529
|
/** The text mermaid's diagram parser reads: the source after mermaid's
|
|
492
|
-
* preprocess
|
|
493
|
-
* whitespace
|
|
530
|
+
* preprocess, its front matter and directives removed and, for all but the
|
|
531
|
+
* KEEPS_COMMENTS types, its comment lines and leading whitespace too. */
|
|
494
532
|
function mermaidCode(source) {
|
|
495
|
-
return
|
|
533
|
+
return preprocess(source).code;
|
|
496
534
|
}
|
|
497
535
|
/**
|
|
498
536
|
* The 1-based source line of the 1-based line `codeLine` of mermaidCode(source).
|
|
@@ -500,18 +538,32 @@ function mermaidCode(source) {
|
|
|
500
538
|
* that spans lines), so the code is the source's lines in order: each code
|
|
501
539
|
* line is matched to the next source line with the same text. A line the
|
|
502
540
|
* preprocess rewrote matches nothing and takes the next line; a line past the
|
|
503
|
-
* end clamps to the last one.
|
|
541
|
+
* end clamps to the last one. Code that kept its comment lines lost no line
|
|
542
|
+
* but the front matter's and a directive's inner line breaks, so its lines
|
|
543
|
+
* are counted instead: matching by text would take a line a directive
|
|
544
|
+
* emptied for a later blank line.
|
|
504
545
|
*/
|
|
505
546
|
function mermaidSourceLine(source, codeLine) {
|
|
506
|
-
const
|
|
507
|
-
const sourceLines =
|
|
508
|
-
|
|
509
|
-
|
|
547
|
+
const { text, frontMatterLines, body, keepsComments, code } = preprocess(source);
|
|
548
|
+
const sourceLines = text.split("\n").map((l) => l.trim());
|
|
549
|
+
if (keepsComments) {
|
|
550
|
+
const joined = /* @__PURE__ */ new Set();
|
|
551
|
+
for (const directive of body.matchAll(DIRECTIVE)) for (let i = directive.index; i < directive.index + directive[0].length; i++) if (body[i] === "\n") joined.add(i);
|
|
552
|
+
let line = frontMatterLines;
|
|
553
|
+
for (let i = 0, breaks = 0; i < body.length && breaks < codeLine - 1; i++) {
|
|
554
|
+
if (body[i] !== "\n") continue;
|
|
555
|
+
line++;
|
|
556
|
+
if (!joined.has(i)) breaks++;
|
|
557
|
+
}
|
|
558
|
+
return Math.min(line, sourceLines.length - 1) + 1;
|
|
559
|
+
}
|
|
560
|
+
const codeLines = code.split("\n");
|
|
561
|
+
let next = frontMatterLines;
|
|
510
562
|
let at = next;
|
|
511
563
|
for (let i = 0; i < Math.min(codeLine, codeLines.length); i++) {
|
|
512
|
-
const
|
|
564
|
+
const lineText = codeLines[i].trim();
|
|
513
565
|
let k = next;
|
|
514
|
-
while (k < sourceLines.length && sourceLines[k] !==
|
|
566
|
+
while (k < sourceLines.length && sourceLines[k] !== lineText) k++;
|
|
515
567
|
at = k < sourceLines.length ? k : next;
|
|
516
568
|
next = at + 1;
|
|
517
569
|
}
|
|
@@ -525,7 +577,7 @@ function mermaidSourceLine(source, codeLine) {
|
|
|
525
577
|
*/
|
|
526
578
|
function mermaidHeader(source) {
|
|
527
579
|
const lines = mermaidCode(source).split("\n");
|
|
528
|
-
const index = lines
|
|
580
|
+
const index = headerIndex(lines);
|
|
529
581
|
if (index < 0) return void 0;
|
|
530
582
|
const text = lines[index].trim();
|
|
531
583
|
return {
|
|
@@ -718,7 +770,7 @@ function checkBpmnXml(raw, opts = {}) {
|
|
|
718
770
|
*
|
|
719
771
|
* Deliberately NOT here: FEEL semantics, hit-policy reachability, dead wiring.
|
|
720
772
|
* Those need the FEEL engine and live in @designiq/decisions (`analyze_decision`),
|
|
721
|
-
* which would drag a browser-side dependency into this
|
|
773
|
+
* which would drag a browser-side dependency into this CLI.
|
|
722
774
|
*/
|
|
723
775
|
function checkDmnXml(raw, opts = {}) {
|
|
724
776
|
const file = opts.file ?? "<dmn>";
|
|
@@ -809,8 +861,8 @@ function mermaidFenceProblem(content, line) {
|
|
|
809
861
|
* notation that is not .bpmn/.dmn): a discovered model must at minimum parse
|
|
810
862
|
* per its notation's mediaKind — a broken .tt/.vc.json used to pass
|
|
811
863
|
* validation untouched. Pure: no filesystem, no process.exit. Deliberately
|
|
812
|
-
* mechanical: notation-specific structure checks
|
|
813
|
-
* checkers (epic #118, step 3).
|
|
864
|
+
* mechanical: notation-specific structure checks are the per-notation
|
|
865
|
+
* checkers' (epic #118, step 3; checkSchemaModel runs this gate first).
|
|
814
866
|
*/
|
|
815
867
|
function checkModelBaseline(raw, opts) {
|
|
816
868
|
const file = opts.file ?? `<${opts.notation}>`;
|
|
@@ -856,18 +908,103 @@ function checkModelBaseline(raw, opts) {
|
|
|
856
908
|
}
|
|
857
909
|
return findings;
|
|
858
910
|
}
|
|
911
|
+
/** a schema model's issue location as the canvases print it ("nodes.1.id"),
|
|
912
|
+
* "(root)" for the document itself */
|
|
913
|
+
const issuePath = (path) => path.join(".") || "(root)";
|
|
914
|
+
const schemaError = (ruleId, file) => (issue) => ({
|
|
915
|
+
severity: "ERROR",
|
|
916
|
+
ruleId,
|
|
917
|
+
file,
|
|
918
|
+
message: `${issuePath(issue.path)}: ${issue.message}`
|
|
919
|
+
});
|
|
920
|
+
function checkTeamTopology(raw, file) {
|
|
921
|
+
const parsed = parseDocument(JSON.parse(raw));
|
|
922
|
+
if (!parsed.ok) return parsed.issues.map(schemaError("team-topology/schema", file));
|
|
923
|
+
serializeDocument(parsed.document);
|
|
924
|
+
return [];
|
|
925
|
+
}
|
|
926
|
+
/**
|
|
927
|
+
* The schema model refuses what the canvas cannot draw (a repeated or
|
|
928
|
+
* reserved id among them), the rest of its strategic-DDD rules are
|
|
929
|
+
* advisory: the canvas opens the map and marks the finding, so a save must
|
|
930
|
+
* not refuse it either.
|
|
931
|
+
*/
|
|
932
|
+
function checkContextMap(raw, file) {
|
|
933
|
+
const parsed = parseDocumentText(raw);
|
|
934
|
+
if (!parsed.ok) return (parsed.issues ?? [{
|
|
935
|
+
path: [],
|
|
936
|
+
message: parsed.error
|
|
937
|
+
}]).map(schemaError("context-map/schema", file));
|
|
938
|
+
serializeDocumentText(parsed.document);
|
|
939
|
+
const { errors, warnings } = validateDocument(parsed.document);
|
|
940
|
+
return [...errors, ...warnings].map((finding) => ({
|
|
941
|
+
severity: "WARN",
|
|
942
|
+
ruleId: "context-map/semantics",
|
|
943
|
+
file,
|
|
944
|
+
message: `${finding.relationshipId === void 0 ? "" : `relationship "${finding.relationshipId}": `}${finding.message} (${finding.rule})`
|
|
945
|
+
}));
|
|
946
|
+
}
|
|
947
|
+
function checkValueChain(raw, file) {
|
|
948
|
+
return documentIssues(JSON.parse(raw)).map(schemaError("value-chain/schema", file));
|
|
949
|
+
}
|
|
950
|
+
/** each JSON notation's schema model, read the way its canvas reads the file
|
|
951
|
+
* (a Map, not an object: the notation id may be a caller's free text such
|
|
952
|
+
* as "constructor") */
|
|
953
|
+
const SCHEMA_CHECKS = /* @__PURE__ */ new Map([
|
|
954
|
+
["team-topology", {
|
|
955
|
+
ruleId: "team-topology/schema",
|
|
956
|
+
check: checkTeamTopology
|
|
957
|
+
}],
|
|
958
|
+
["context-map", {
|
|
959
|
+
ruleId: "context-map/schema",
|
|
960
|
+
check: checkContextMap
|
|
961
|
+
}],
|
|
962
|
+
["value-chain", {
|
|
963
|
+
ruleId: "value-chain/schema",
|
|
964
|
+
check: checkValueChain
|
|
965
|
+
}]
|
|
966
|
+
]);
|
|
967
|
+
/**
|
|
968
|
+
* The schema check of a Miragon JSON notation (Team Topology, Context Map,
|
|
969
|
+
* Value Chain): the file is parsed with the notation's schema model, the
|
|
970
|
+
* same parse its canvas opens it with, so everything the model refuses (a
|
|
971
|
+
* duplicate or reserved id, a coordinate out of range, a newer version) is
|
|
972
|
+
* an ERROR — a save would otherwise land a file the canvas cannot open.
|
|
973
|
+
* Text that is no JSON keeps the baseline's finding, and a notation without
|
|
974
|
+
* a schema model gets the baseline alone. Pure: no filesystem, no
|
|
975
|
+
* process.exit.
|
|
976
|
+
*/
|
|
977
|
+
function checkSchemaModel(raw, opts) {
|
|
978
|
+
const baseline = checkModelBaseline(raw, opts);
|
|
979
|
+
const schema = SCHEMA_CHECKS.get(opts.notation);
|
|
980
|
+
if (!schema || baseline.length > 0 || byId(opts.notation)?.docShape === "structured") return baseline;
|
|
981
|
+
const file = opts.file ?? `<${opts.notation}>`;
|
|
982
|
+
try {
|
|
983
|
+
return schema.check(raw, file);
|
|
984
|
+
} catch (error) {
|
|
985
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
986
|
+
return [{
|
|
987
|
+
severity: "ERROR",
|
|
988
|
+
ruleId: schema.ruleId,
|
|
989
|
+
file,
|
|
990
|
+
message: `(root): could not be checked: ${reason}`
|
|
991
|
+
}];
|
|
992
|
+
}
|
|
993
|
+
}
|
|
859
994
|
/**
|
|
860
995
|
* THE one check dispatch: the platform check for whatever notation `ctx.path`
|
|
861
|
-
* is — full checkers for BPMN/DMN, the
|
|
862
|
-
*
|
|
863
|
-
*
|
|
864
|
-
* (the file is not a model;
|
|
865
|
-
*
|
|
996
|
+
* is — full checkers for BPMN/DMN, the schema model for the Miragon JSON
|
|
997
|
+
* notations, the mediaKind baseline for everything else, plus the generic
|
|
998
|
+
* dangling-reference rule for ANY notation with a refs emitter; `undefined`
|
|
999
|
+
* when the path matches no registered notation (the file is not a model;
|
|
1000
|
+
* nothing to check) or `ctx.notation` names none (a mistyped id must not
|
|
1001
|
+
* pass a file unchecked). Consumed by the CLI (runCli) AND the Live Host's
|
|
1002
|
+
* save gate (application/content.ts) so both always agree.
|
|
866
1003
|
*/
|
|
867
1004
|
function checkModel(raw, ctx) {
|
|
868
1005
|
const notation = ctx.notation ?? byExtension(ctx.path)?.id;
|
|
869
|
-
if (!notation) return void 0;
|
|
870
|
-
return [...notation === "bpmn" ? checkBpmnXml(raw, { file: ctx.path }).findings : notation === "dmn" ? checkDmnXml(raw, { file: ctx.path }).findings :
|
|
1006
|
+
if (!notation || !byId(notation)) return void 0;
|
|
1007
|
+
return [...notation === "bpmn" ? checkBpmnXml(raw, { file: ctx.path }).findings : notation === "dmn" ? checkDmnXml(raw, { file: ctx.path }).findings : checkSchemaModel(raw, {
|
|
871
1008
|
file: ctx.path,
|
|
872
1009
|
notation
|
|
873
1010
|
}), ...checkRefs(notation, raw, ctx)];
|
|
@@ -916,7 +1053,7 @@ async function runCli() {
|
|
|
916
1053
|
/** optional single-model filter (a model file stem) */
|
|
917
1054
|
const only = argv[0];
|
|
918
1055
|
const rel = (p) => p.startsWith("/") ? relative(ROOT, p) : p;
|
|
919
|
-
const { CONTENT_CONFIG_FILE, contentConfigConflict, discoverModels, legacyContentConfigFile, loadContentConfig } = await import("./content-
|
|
1056
|
+
const { CONTENT_CONFIG_FILE, contentConfigConflict, discoverModels, legacyContentConfigFile, loadContentConfig } = await import("./content-B0u0kwZN.js");
|
|
920
1057
|
const cfg = loadContentConfig(ROOT);
|
|
921
1058
|
if (!cfg) {
|
|
922
1059
|
console.error(notContentRepoError(ROOT));
|
|
@@ -979,4 +1116,4 @@ async function runCli() {
|
|
|
979
1116
|
process.exit(errorCount === 0 ? 0 : 1);
|
|
980
1117
|
}
|
|
981
1118
|
//#endregion
|
|
982
|
-
export {
|
|
1119
|
+
export { checkSchemaModel as a, checkModelBaseline as i, checkDmnXml as n, runCli as o, checkModel as r, checkBpmnXml as t };
|
package/dist/validate.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { t as checkBpmnXml, n as checkDmnXml, r as checkModel, i as checkModelBaseline, a as runCli } from "./validate-
|
|
1
|
+
export { t as checkBpmnXml, n as checkDmnXml, r as checkModel, i as checkModelBaseline, a as checkSchemaModel, o as runCli } from "./validate-CUTctX4d.js";
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miragon/design-iq-validator",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.7.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Platform-owned validator for designIQ content repos — designiq.yml discovery, BPMN/DMN structure and diagram coverage, cross-model references, and a parse check for every other notation. Runs against any checkout via --root; never executes content-repo code.",
|
|
5
|
+
"description": "Platform-owned validator for designIQ content repos — designiq.yml discovery, BPMN/DMN structure and diagram coverage, cross-model references, the schema check of Team Topologies, Context Maps and Value Chains, and a parse check for every other notation. Runs against any checkout via --root; never executes content-repo code.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -32,10 +32,14 @@
|
|
|
32
32
|
"publishConfig": {
|
|
33
33
|
"access": "public"
|
|
34
34
|
},
|
|
35
|
-
"//": "fast-xml-parser stays a RUNTIME dependency although src/ no longer imports it directly: tsdown inlines @designiq/notations (whose extract.ts imports it) but keeps npm deps external, so the published dist/ resolves it from here. Removing it breaks the npm artifact at import time while the workspace keeps working.",
|
|
35
|
+
"//": "fast-xml-parser stays a RUNTIME dependency although src/ no longer imports it directly: tsdown inlines @designiq/notations (whose extract.ts imports it) but keeps npm deps external, so the published dist/ resolves it from here. Removing it breaks the npm artifact at import time while the workspace keeps working. zod is a RUNTIME dependency for the same reason: src/ never imports it, but the team-topologies, context-maps and value-chain schema models take it as a peer, and the published package is what provides it.",
|
|
36
36
|
"dependencies": {
|
|
37
|
+
"@miragon/context-maps-schema-model": "0.4.0",
|
|
38
|
+
"@miragon/team-topologies-schema-model": "0.12.0",
|
|
39
|
+
"@miragon/value-chain-schema-model": "0.6.0",
|
|
37
40
|
"fast-xml-parser": "5.11.2",
|
|
38
|
-
"yaml": "2.9.1"
|
|
41
|
+
"yaml": "2.9.1",
|
|
42
|
+
"zod": "4.6.7"
|
|
39
43
|
},
|
|
40
44
|
"devDependencies": {
|
|
41
45
|
"tsdown": "0.23.1",
|