dsh-model-everything 0.0.0-stage → 0.2.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 (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.en-US.md +332 -0
  3. package/README.md +332 -3
  4. package/cordis.patch.yml +20 -0
  5. package/examples/README.md +48 -0
  6. package/examples/control-dispatcher.json +113 -0
  7. package/examples/control-dispatcher.overbudget.json +113 -0
  8. package/examples/ecommerce-data-model.json +185 -0
  9. package/examples/order-state-machine.after.json +116 -0
  10. package/examples/order-state-machine.before.json +101 -0
  11. package/examples/user-migration.after.json +29 -0
  12. package/examples/user-migration.before.json +29 -0
  13. package/gate/locale/en.json +6 -0
  14. package/gate/locale/zh.json +6 -0
  15. package/gate/package.json +5 -0
  16. package/lib/baseline.js +249 -0
  17. package/lib/compose-tool.js +80 -0
  18. package/lib/concurrency-tool.js +40 -0
  19. package/lib/concurrency.js +206 -0
  20. package/lib/data-engine.js +1141 -0
  21. package/lib/data-tool.js +87 -0
  22. package/lib/design-engine.js +1149 -0
  23. package/lib/design-tool.js +63 -0
  24. package/lib/engine.js +4343 -0
  25. package/lib/export-tool.js +78 -0
  26. package/lib/exporters.js +434 -0
  27. package/lib/gate.js +274 -0
  28. package/lib/granularity.js +237 -0
  29. package/lib/index.js +237 -0
  30. package/lib/json-value.js +18 -0
  31. package/lib/model-file.js +48 -0
  32. package/lib/option-shape.js +62 -0
  33. package/lib/report-diff-tool.js +63 -0
  34. package/lib/report-view.js +433 -0
  35. package/lib/structure-tool.js +91 -0
  36. package/lib/structure.js +711 -0
  37. package/lib/tool.js +94 -0
  38. package/lib/types/baseline.d.ts +65 -0
  39. package/lib/types/compose-tool.d.ts +7 -0
  40. package/lib/types/concurrency-tool.d.ts +8 -0
  41. package/lib/types/concurrency.d.ts +37 -0
  42. package/lib/types/data-engine.d.ts +211 -0
  43. package/lib/types/data-tool.d.ts +10 -0
  44. package/lib/types/design-engine.d.ts +215 -0
  45. package/lib/types/design-tool.d.ts +11 -0
  46. package/lib/types/engine.d.ts +494 -0
  47. package/lib/types/export-tool.d.ts +7 -0
  48. package/lib/types/exporters.d.ts +37 -0
  49. package/lib/types/gate.d.ts +91 -0
  50. package/lib/types/granularity.d.ts +106 -0
  51. package/lib/types/index.d.ts +45 -0
  52. package/lib/types/json-value.d.ts +21 -0
  53. package/lib/types/model-file.d.ts +7 -0
  54. package/lib/types/option-shape.d.ts +22 -0
  55. package/lib/types/report-diff-tool.d.ts +8 -0
  56. package/lib/types/report-view.d.ts +50 -0
  57. package/lib/types/structure-tool.d.ts +10 -0
  58. package/lib/types/structure.d.ts +152 -0
  59. package/lib/types/tool.d.ts +11 -0
  60. package/lib/types/uml-communication.d.ts +109 -0
  61. package/lib/types/uml-composite.d.ts +107 -0
  62. package/lib/types/uml-interaction-overview.d.ts +91 -0
  63. package/lib/types/uml-profile.d.ts +112 -0
  64. package/lib/types/uml-structure.d.ts +53 -0
  65. package/lib/types/uml-syntax.d.ts +296 -0
  66. package/lib/types/uml-timing.d.ts +152 -0
  67. package/lib/types/uml-tool.d.ts +12 -0
  68. package/lib/types/uml-usecase.d.ts +58 -0
  69. package/lib/types/uml.d.ts +303 -0
  70. package/lib/uml-communication.js +353 -0
  71. package/lib/uml-composite.js +442 -0
  72. package/lib/uml-interaction-overview.js +277 -0
  73. package/lib/uml-profile.js +373 -0
  74. package/lib/uml-structure.js +306 -0
  75. package/lib/uml-syntax.js +545 -0
  76. package/lib/uml-timing.js +974 -0
  77. package/lib/uml-tool.js +213 -0
  78. package/lib/uml-usecase.js +532 -0
  79. package/lib/uml.js +2110 -0
  80. package/locale/en.json +6 -0
  81. package/locale/zh.json +6 -0
  82. package/package.json +128 -6
  83. package/skills/model-everything/SKILL.md +450 -0
  84. package/skills/model-everything/references/dsh-model-schema.md +437 -0
  85. package/skills/model-everything/references/gap-routing-guide.md +42 -0
  86. package/skills/model-everything/references/hash-spec.md +152 -0
  87. package/skills/model-everything/references/logic-verification-guide.md +422 -0
  88. package/skills/model-everything-datamodel/SKILL.md +130 -0
  89. package/skills/model-everything-datamodel/references/data-model-guide.md +84 -0
  90. package/skills/model-everything-datamodel/references/data-model-schema.md +128 -0
  91. package/skills/model-everything-design/SKILL.md +115 -0
  92. package/skills/model-everything-interleaving/SKILL.md +63 -0
  93. package/skills/model-everything-interleaving/SKILL.zh-CN.md +60 -0
  94. package/skills/model-everything-interleaving/references/concurrency-risk-guide.md +63 -0
  95. package/skills/model-everything-report-reading/SKILL.md +137 -0
  96. package/skills/model-everything-report-reading/references/reading-a-report.md +166 -0
  97. package/skills/model-everything-report-reading/references/reading-a-report.mmd +59 -0
  98. package/skills/model-everything-structure/SKILL.md +96 -0
  99. package/skills/model-everything-structure/references/structure-review-guide.md +201 -0
  100. package/skills/model-everything-uml/SKILL.md +60 -0
  101. package/skills/model-everything-uml/references/uml-modeling-guide.md +551 -0
  102. package/src/baseline.ts +288 -0
  103. package/src/compose-tool.ts +82 -0
  104. package/src/concurrency-tool.ts +44 -0
  105. package/src/concurrency.ts +254 -0
  106. package/src/data-engine.ts +1208 -0
  107. package/src/data-tool.ts +91 -0
  108. package/src/design-engine.ts +1263 -0
  109. package/src/design-tool.ts +68 -0
  110. package/src/engine.ts +4564 -0
  111. package/src/export-tool.ts +81 -0
  112. package/src/exporters.ts +399 -0
  113. package/src/gate.ts +330 -0
  114. package/src/granularity.ts +312 -0
  115. package/src/index.ts +289 -0
  116. package/src/json-value.ts +20 -0
  117. package/src/model-file.ts +49 -0
  118. package/src/option-shape.ts +64 -0
  119. package/src/report-diff-tool.ts +67 -0
  120. package/src/report-view.ts +483 -0
  121. package/src/structure-tool.ts +93 -0
  122. package/src/structure.ts +786 -0
  123. package/src/tool.ts +103 -0
  124. package/src/uml-communication.ts +428 -0
  125. package/src/uml-composite.ts +520 -0
  126. package/src/uml-interaction-overview.ts +321 -0
  127. package/src/uml-profile.ts +439 -0
  128. package/src/uml-structure.ts +317 -0
  129. package/src/uml-syntax.ts +764 -0
  130. package/src/uml-timing.ts +1106 -0
  131. package/src/uml-tool.ts +230 -0
  132. package/src/uml-usecase.ts +566 -0
  133. package/src/uml.ts +2139 -0
@@ -0,0 +1,306 @@
1
+ /**
2
+ * The structure families: class, object, package, component and deployment diagrams.
3
+ *
4
+ * A structure diagram is not a machine and not a use case list: it is a graph of
5
+ * nodes and the edges between them. The five families differ in vocabulary, not in
6
+ * shape, so they share one module factory and one model.
7
+ *
8
+ * Parsing is not reimplemented here. `structure.ts` already reads this text as a
9
+ * dependency graph, and it is the single source for that reading: this module calls
10
+ * it, then gives the result the family's name. Reviewing does the same - the graph is
11
+ * rendered back and handed to `reviewStructure`, so a structure diagram reviewed
12
+ * through the UML front end and one reviewed through `uml_structure_verify`
13
+ * report the same defects with the same codes.
14
+ *
15
+ * @module model-everything-uml-structure
16
+ */
17
+ import { UmlError, UML_PLAIN_ID, directiveLine, readUmlDirectives, umlSourceLines, } from './uml-syntax.js';
18
+ import { parseStructure, reviewStructure } from './structure.js';
19
+ const STRUCTURE_FAMILIES = ['class', 'object', 'package', 'component', 'deployment'];
20
+ /** PlantUML keyword each kind is drawn with; the first is the fallback for the family. */
21
+ const PLANTUML_KEYWORDS = {
22
+ class: 'class', interface: 'interface', enum: 'enum', object: 'object', package: 'package',
23
+ component: 'component', node: 'node', artifact: 'artifact', database: 'database', actor: 'actor',
24
+ rectangle: 'rectangle', cloud: 'cloud', queue: 'queue', usecase: 'usecase', unknown: 'class',
25
+ };
26
+ const FAMILY_FALLBACK_KIND = {
27
+ class: 'class', object: 'object', package: 'package', component: 'component', deployment: 'node',
28
+ };
29
+ const RESERVED_IDS = new Set(['class', 'classDiagram', 'end', 'graph', 'subgraph', 'direction', 'style', 'click', 'note', 'hide', 'show', 'title', 'namespace']);
30
+ function isPlainId(id) {
31
+ return UML_PLAIN_ID.test(id) && !RESERVED_IDS.has(id.toLowerCase());
32
+ }
33
+ export function validateStructureModel(input, family) {
34
+ if (input === null || typeof input !== 'object' || Array.isArray(input)) {
35
+ throw new UmlError('a structure model must be an object with schemaVersion 1, nodes and edges', 'UML_INPUT');
36
+ }
37
+ const model = input;
38
+ if (model.schemaVersion !== 1)
39
+ throw new UmlError('a structure model must declare schemaVersion 1', 'UML_INPUT');
40
+ const declaredFamily = model.family;
41
+ if (declaredFamily !== undefined && !STRUCTURE_FAMILIES.includes(declaredFamily)) {
42
+ throw new UmlError('unknown structure family "' + String(declaredFamily) + '"; expected ' + STRUCTURE_FAMILIES.join(' | '), 'UML_INPUT');
43
+ }
44
+ if (declaredFamily !== undefined && declaredFamily !== family) {
45
+ throw new UmlError('this model is a ' + declaredFamily + ' diagram, and it was handed to the ' + family + ' module: render and review it as the family it declares.', 'UML_INPUT');
46
+ }
47
+ if (!Array.isArray(model.nodes) || !Array.isArray(model.edges)) {
48
+ // A model with neither field is not an empty structure diagram: it is some other
49
+ // family's model, and reading it as an empty diagram would hide that.
50
+ throw new UmlError('a structure model must declare nodes and edges as arrays', 'UML_INPUT');
51
+ }
52
+ const nodes = model.nodes;
53
+ const edges = model.edges;
54
+ const ids = new Set();
55
+ for (const node of nodes) {
56
+ if (node === null || typeof node !== 'object' || typeof node.id !== 'string' || node.id.trim() === '') {
57
+ throw new UmlError('every node needs a non-empty string id', 'UML_INPUT');
58
+ }
59
+ if (ids.has(node.id))
60
+ throw new UmlError('duplicate node id "' + node.id + '"', 'UML_INPUT');
61
+ ids.add(node.id);
62
+ }
63
+ for (const edge of edges) {
64
+ if (edge === null || typeof edge !== 'object')
65
+ throw new UmlError('every edge must be an object', 'UML_INPUT');
66
+ if (!ids.has(edge.from) || !ids.has(edge.to)) {
67
+ throw new UmlError('edge ' + edge.from + ' -> ' + edge.to + ' names a node the model does not declare', 'UML_INPUT');
68
+ }
69
+ }
70
+ const entries = nodes.map((node) => ({ id: node.id, kind: typeof node.kind === 'string' && node.kind !== '' ? node.kind : FAMILY_FALLBACK_KIND[family], ...(node.label === undefined ? {} : { label: node.label }), ...(node.parent === undefined ? {} : { parent: node.parent }) }));
71
+ // Canonical order: a container precedes what it contains, which is the order the
72
+ // renderer can draw. A model that arrives with a child first is normalised rather
73
+ // than reported as a fidelity difference the notation cannot actually express.
74
+ const known = new Set(entries.map((entry) => entry.id));
75
+ const canonical = [];
76
+ const emitted = new Set();
77
+ const emit = (entry) => {
78
+ if (emitted.has(entry.id))
79
+ return;
80
+ emitted.add(entry.id);
81
+ canonical.push(entry);
82
+ for (const child of entries)
83
+ if (child.parent === entry.id)
84
+ emit(child);
85
+ };
86
+ for (const entry of entries)
87
+ if (entry.parent === undefined || entry.parent === entry.id || !known.has(entry.parent))
88
+ emit(entry);
89
+ for (const entry of entries)
90
+ emit(entry);
91
+ return {
92
+ schemaVersion: 1,
93
+ family,
94
+ nodes: canonical,
95
+ edges: edges.map((edge) => ({ from: edge.from, to: edge.to, ...(edge.label === undefined ? {} : { label: edge.label }) })),
96
+ };
97
+ }
98
+ function graphToModel(graph, family) {
99
+ return {
100
+ schemaVersion: 1,
101
+ family,
102
+ nodes: graph.nodes.map((node) => ({
103
+ id: node.id,
104
+ kind: node.kind === 'unknown' ? FAMILY_FALLBACK_KIND[family] : node.kind,
105
+ ...(node.label === undefined ? {} : { label: node.label }),
106
+ ...(node.parent === undefined ? {} : { parent: node.parent }),
107
+ })),
108
+ edges: graph.edges.map((edge) => ({ from: edge.from, to: edge.to, ...(edge.label === undefined ? {} : { label: edge.label }) })),
109
+ };
110
+ }
111
+ // ---------------------------------------------------------------------------
112
+ // Parsing
113
+ // ---------------------------------------------------------------------------
114
+ export function parseStructureFamily(text, notation, family) {
115
+ const graph = parseStructure(text, notation);
116
+ const directives = readUmlDirectives(text.split(/\r?\n/), notation);
117
+ const alias = new Map();
118
+ for (const value of directives.values.get('alias') ?? []) {
119
+ const split = value.indexOf(' ');
120
+ if (split > 0)
121
+ alias.set(value.slice(0, split).trim(), value.slice(split + 1).trim());
122
+ }
123
+ const parents = new Map();
124
+ for (const value of directives.values.get('parent') ?? []) {
125
+ const split = value.indexOf(' ');
126
+ if (split > 0)
127
+ parents.set(value.slice(0, split).trim(), value.slice(split + 1).trim());
128
+ }
129
+ const kinds = new Map();
130
+ for (const value of directives.values.get('kind') ?? []) {
131
+ const split = value.indexOf(' ');
132
+ if (split > 0)
133
+ kinds.set(value.slice(0, split).trim(), value.slice(split + 1).trim());
134
+ }
135
+ const restore = (id) => alias.get(id) ?? id;
136
+ const labels = {};
137
+ const nodes = graph.nodes.map((node) => {
138
+ const id = restore(node.id);
139
+ const label = node.label === undefined || node.label === id ? undefined : node.label;
140
+ if (label !== undefined)
141
+ labels[id] = label;
142
+ const pinned = kinds.get(node.id);
143
+ const kind = pinned ?? (node.kind === 'unknown' ? FAMILY_FALLBACK_KIND[family] : node.kind);
144
+ const declaredParent = parents.get(node.id);
145
+ const parent = declaredParent !== undefined ? declaredParent : (node.parent === undefined ? undefined : restore(node.parent));
146
+ return { id, kind, ...(label === undefined ? {} : { label }), ...(parent === undefined ? {} : { parent }) };
147
+ });
148
+ const edges = graph.edges.map((edge) => ({
149
+ from: restore(edge.from),
150
+ to: restore(edge.to),
151
+ ...(edge.label === undefined ? {} : { label: edge.label }),
152
+ }));
153
+ return {
154
+ model: validateStructureModel({ schemaVersion: 1, family, nodes, edges }, family),
155
+ labels,
156
+ warnings: [...graph.warnings],
157
+ discardedConstructs: [],
158
+ discardedEdges: 0,
159
+ };
160
+ }
161
+ // ---------------------------------------------------------------------------
162
+ // Rendering
163
+ // ---------------------------------------------------------------------------
164
+ /** Which keyword draws this kind, in the family it was declared in. */
165
+ function keywordFor(kind, family) {
166
+ return PLANTUML_KEYWORDS[kind] ?? FAMILY_FALLBACK_KIND[family];
167
+ }
168
+ export function renderStructureFamily(input, notation, family) {
169
+ const model = validateStructureModel(input, family);
170
+ const warnings = [];
171
+ const taken = new Set();
172
+ const alias = new Map();
173
+ const aliasFor = (id) => {
174
+ if (isPlainId(id) && !taken.has(id)) {
175
+ taken.add(id);
176
+ return id;
177
+ }
178
+ let index = 1;
179
+ let candidate = 'N' + String(index);
180
+ while (taken.has(candidate)) {
181
+ index += 1;
182
+ candidate = 'N' + String(index);
183
+ }
184
+ taken.add(candidate);
185
+ alias.set(candidate, id);
186
+ return candidate;
187
+ };
188
+ if (notation === 'mermaid') {
189
+ if (family !== 'class') {
190
+ throw new UmlError('mermaid has no ' + family + ' diagram: its nearer views (architecture-beta, C4) use another vocabulary, and drawing one here would mislabel the model. Render it with notation "plantuml".', 'UML_VIEW_UNREPRESENTABLE');
191
+ }
192
+ const lines = ['classDiagram'];
193
+ lines.push(directiveLine('mermaid', 'uml v1 notation=mermaid family=' + family));
194
+ const notationIds = model.nodes.map((node) => aliasFor(node.id));
195
+ model.nodes.forEach((node, index) => lines.push(directiveLine('mermaid', 'kind ' + notationIds[index] + ' ' + node.kind)));
196
+ model.nodes.forEach((node, index) => { if (node.parent !== undefined)
197
+ lines.push(directiveLine('mermaid', 'parent ' + notationIds[index] + ' ' + node.parent)); });
198
+ for (const [notationId, modelId] of alias)
199
+ lines.push(directiveLine('mermaid', 'alias ' + notationId + ' ' + modelId));
200
+ model.nodes.forEach((node, index) => {
201
+ const id = notationIds[index];
202
+ if (/["\n\r]/.test(node.label ?? '')) {
203
+ warnings.push('UML_RENDER_LABEL_UNSAFE: the label of "' + node.id + '" contains a quote or a line break; it was written with single quotes instead and will not read back verbatim.');
204
+ }
205
+ const label = (node.label ?? '').replace(/["\n\r]+/g, "'");
206
+ lines.push(label === '' || label === id ? ' class ' + id : ' class ' + id + '["' + label + '"]');
207
+ });
208
+ for (const edge of model.edges) {
209
+ const idOf = new Map(model.nodes.map((node, index) => [node.id, notationIds[index]]));
210
+ lines.push(' ' + (idOf.get(edge.from) ?? edge.from) + ' --> ' + (idOf.get(edge.to) ?? edge.to) + (edge.label === undefined ? '' : ' : ' + edge.label));
211
+ }
212
+ return { primary: lines.join('\n') + '\n', warnings };
213
+ }
214
+ const lines = ['@startuml'];
215
+ lines.push(directiveLine('plantuml', 'uml v1 notation=plantuml family=' + family));
216
+ const notationIds = model.nodes.map((node) => aliasFor(node.id));
217
+ model.nodes.forEach((node, index) => lines.push(directiveLine('plantuml', 'kind ' + notationIds[index] + ' ' + node.kind)));
218
+ model.nodes.forEach((node, index) => { if (node.parent !== undefined)
219
+ lines.push(directiveLine('plantuml', 'parent ' + notationIds[index] + ' ' + node.parent)); });
220
+ for (const [notationId, modelId] of alias)
221
+ lines.push(directiveLine('plantuml', 'alias ' + notationId + ' ' + modelId));
222
+ const position = new Map(model.nodes.map((node, index) => [node.id, index]));
223
+ const children = new Map();
224
+ const roots = [];
225
+ const known = new Set(model.nodes.map((node) => node.id));
226
+ for (const node of model.nodes) {
227
+ const parent = node.parent !== undefined && node.parent !== node.id && known.has(node.parent) ? node.parent : undefined;
228
+ if (parent === undefined) {
229
+ roots.push(node);
230
+ continue;
231
+ }
232
+ const list = children.get(parent);
233
+ if (list === undefined)
234
+ children.set(parent, [node]);
235
+ else
236
+ list.push(node);
237
+ }
238
+ const declare = (node, indent) => {
239
+ const id = notationIds[position.get(node.id)];
240
+ const keyword = keywordFor(node.kind, family);
241
+ const label = (node.label ?? '').replace(/["\n\r]+/g, "'");
242
+ if ((node.label ?? '') !== label)
243
+ warnings.push('UML_RENDER_LABEL_UNSAFE: the label of "' + node.id + '" contains a quote or a line break; it was written with single quotes instead and will not read back verbatim.');
244
+ const members = children.get(node.id) ?? [];
245
+ const head = label === '' || label === id ? keyword + ' ' + id : keyword + ' "' + label + '" as ' + id;
246
+ if (members.length === 0) {
247
+ lines.push(indent + head);
248
+ return;
249
+ }
250
+ lines.push(indent + head + ' {');
251
+ for (const member of members)
252
+ declare(member, indent + ' ');
253
+ lines.push(indent + '}');
254
+ };
255
+ for (const root of roots)
256
+ declare(root, '');
257
+ const idOf = new Map(model.nodes.map((node, index) => [node.id, notationIds[index]]));
258
+ for (const edge of model.edges) {
259
+ lines.push((idOf.get(edge.from) ?? edge.from) + ' --> ' + (idOf.get(edge.to) ?? edge.to) + (edge.label === undefined ? '' : ' : ' + edge.label));
260
+ }
261
+ lines.push('@enduml');
262
+ return { primary: lines.join('\n') + '\n', warnings };
263
+ }
264
+ // ---------------------------------------------------------------------------
265
+ // Review
266
+ // ---------------------------------------------------------------------------
267
+ /**
268
+ * The review is the structure tool's, reached through a render.
269
+ *
270
+ * A structure diagram's defects are properties of its graph, and `reviewStructure`
271
+ * already checks them (isolated node, dangling endpoint, cycle, and the matrix rules
272
+ * when one is declared). Re-deriving them here would be a second implementation of
273
+ * the same checks, and the two would drift. So the model is rendered back and handed
274
+ * over, which also proves the round trip on the way.
275
+ */
276
+ export function reviewStructureFamily(model, labels, notation) {
277
+ const rendered = renderStructureFamily(model, notation, model.family);
278
+ const report = reviewStructure({ diagram: rendered.primary, notation });
279
+ const findings = report.findings.map((finding) => ({
280
+ code: finding.code,
281
+ severity: finding.severity,
282
+ message: finding.message,
283
+ ...(finding.detail === undefined ? {} : { detail: finding.detail }),
284
+ ...(finding.evidence === undefined ? {} : { evidence: finding.evidence }),
285
+ }));
286
+ for (const node of model.nodes) {
287
+ if (node.label !== undefined || labels[node.id] !== undefined)
288
+ continue;
289
+ findings.push({
290
+ code: 'UML040_STRUCTURE_NODE_UNDOCUMENTED',
291
+ severity: 'info',
292
+ message: 'node ' + node.id + ' carries no name a reader can map back to the code: give it a display label.',
293
+ evidence: { id: node.id },
294
+ });
295
+ }
296
+ return findings;
297
+ }
298
+ /** One module per structure family: they differ in vocabulary, not in shape. */
299
+ export function structureFamilyModule(family) {
300
+ return {
301
+ id: family,
302
+ render: (input, notation, _options) => renderStructureFamily(input, notation, family),
303
+ parse: (text, notation) => parseStructureFamily(text, notation, family),
304
+ review: (model, labels, notation) => reviewStructureFamily(model, labels, notation),
305
+ };
306
+ }