@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 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), and a per-mediaKind parse baseline for
7
- every other registered notation (a broken `.tt`/`.vc.json` is an ERROR; DSL notations are
8
- lenient by design; a `mermaid` fence in a Markdown document that does not start with a diagram
9
- type such as `flowchart` or `sequenceDiagram` is a WARN, `mermaid/diagram-type`, naming the
10
- document's line). Every finding carries a stable `ruleId` (e.g. `bpmn/flow`, `dmn/di`,
11
- `refs/dangling`). It treats the target repo as pure data — it never executes content-repo code.
12
- Exit code 0 = no errors (warnings allowed), 1 = errors.
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,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as runCli } from "./validate-DjtZJTGk.js";
2
+ import { o as runCli } from "./validate-CUTctX4d.js";
3
3
  //#region src/cli.ts
4
4
  /**
5
5
  * CLI entry for the platform validator. A dedicated entry file (instead of an
@@ -1 +1 @@
1
- export { t as CONTENT_CONFIG_FILE, i as contentConfigConflict, a as discoverModels, o as legacyContentConfigFile, s as loadContentConfig } from "./content-Bsv9ZLFd.js";
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: (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
- 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.label ?? el.name
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-Bsv9ZLFd.js";
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 (front matter, directives and comment lines removed, leading
493
- * whitespace trimmed). */
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 cleanup(source).replace(FRONT_MATTER, "").replace(DIRECTIVE, "").replace(COMMENT_LINE, "").trimStart();
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 cleaned = cleanup(source);
507
- const sourceLines = cleaned.split("\n").map((l) => l.trim());
508
- const codeLines = mermaidCode(source).split("\n");
509
- let next = (cleaned.match(FRONT_MATTER)?.[0] ?? "").split("\n").length - 1;
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 text = codeLines[i].trim();
564
+ const lineText = codeLines[i].trim();
513
565
  let k = next;
514
- while (k < sourceLines.length && sourceLines[k] !== text) 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.findIndex((l) => l.trim() !== "" && !l.trim().startsWith("%%"));
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 zero-dep CLI.
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 arrive with the per-notation
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 mediaKind baseline for everything
862
- * else, plus the generic dangling-reference rule for ANY notation with a
863
- * refs emitter; `undefined` when the path matches no registered notation
864
- * (the file is not a model; nothing to check). Consumed by the CLI (runCli)
865
- * AND the Live Host's save gate (application/content.ts) so both always agree.
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 : checkModelBaseline(raw, {
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-wjC_epKd.js");
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 { runCli as a, checkModelBaseline as i, checkDmnXml as n, checkModel as r, checkBpmnXml as t };
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-DjtZJTGk.js";
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.5.0",
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",