@agilewallaby/c4-model 2.2.0 → 2.3.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/package.json +5 -3
- package/src/index.cjs +952 -0
- package/src/index.js +88 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agilewallaby/c4-model",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -14,12 +14,14 @@
|
|
|
14
14
|
"testcontainers": "^10.28.0"
|
|
15
15
|
},
|
|
16
16
|
"type": "module",
|
|
17
|
-
"main": "./src/index.
|
|
17
|
+
"main": "./src/index.cjs",
|
|
18
|
+
"module": "./src/index.js",
|
|
18
19
|
"types": "./src/index.d.ts",
|
|
19
20
|
"exports": {
|
|
20
21
|
".": {
|
|
22
|
+
"types": "./src/index.d.ts",
|
|
21
23
|
"import": "./src/index.js",
|
|
22
|
-
"
|
|
24
|
+
"require": "./src/index.cjs"
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
}
|
package/src/index.cjs
ADDED
|
@@ -0,0 +1,952 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// libs/c4-model/src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
Component: () => Component,
|
|
34
|
+
Container: () => Container,
|
|
35
|
+
ContainerGroup: () => ContainerGroup,
|
|
36
|
+
ElementArchetype: () => ElementArchetype,
|
|
37
|
+
Model: () => Model,
|
|
38
|
+
ModelGroup: () => ModelGroup,
|
|
39
|
+
Person: () => Person,
|
|
40
|
+
RelationshipArchetype: () => RelationshipArchetype,
|
|
41
|
+
SoftwareSystem: () => SoftwareSystem,
|
|
42
|
+
SoftwareSystemGroup: () => SoftwareSystemGroup,
|
|
43
|
+
StructurizrDSLWriter: () => StructurizrDSLWriter,
|
|
44
|
+
View: () => View,
|
|
45
|
+
Views: () => Views,
|
|
46
|
+
buildModel: () => buildModel,
|
|
47
|
+
buildModelWithCatalog: () => buildModelWithCatalog,
|
|
48
|
+
generateDiagrams: () => generateDiagrams,
|
|
49
|
+
mergeArchetypeWithOverride: () => mergeArchetypeWithOverride
|
|
50
|
+
});
|
|
51
|
+
module.exports = __toCommonJS(index_exports);
|
|
52
|
+
|
|
53
|
+
// libs/c4-model/src/archetype.ts
|
|
54
|
+
var ElementArchetype = class {
|
|
55
|
+
constructor(name, elementKind, definition, parent) {
|
|
56
|
+
this.name = name;
|
|
57
|
+
this.elementKind = elementKind;
|
|
58
|
+
this.parent = parent;
|
|
59
|
+
this.ownDescription = definition?.description;
|
|
60
|
+
this.ownTechnology = definition?.technology;
|
|
61
|
+
this.ownTags = definition?.tags ?? [];
|
|
62
|
+
this.description = this.ownDescription ?? parent?.description;
|
|
63
|
+
this.technology = this.ownTechnology ?? parent?.technology;
|
|
64
|
+
this.tags = [...parent?.tags ?? [], ...this.ownTags];
|
|
65
|
+
}
|
|
66
|
+
ownDescription;
|
|
67
|
+
ownTechnology;
|
|
68
|
+
ownTags;
|
|
69
|
+
description;
|
|
70
|
+
technology;
|
|
71
|
+
tags;
|
|
72
|
+
};
|
|
73
|
+
var RelationshipArchetype = class {
|
|
74
|
+
constructor(name, definition, parent) {
|
|
75
|
+
this.name = name;
|
|
76
|
+
this.parent = parent;
|
|
77
|
+
this.ownDescription = definition?.description;
|
|
78
|
+
this.ownTechnology = definition?.technology;
|
|
79
|
+
this.ownTags = definition?.tags ?? [];
|
|
80
|
+
this.description = this.ownDescription ?? parent?.description;
|
|
81
|
+
this.technology = this.ownTechnology ?? parent?.technology;
|
|
82
|
+
this.tags = [...parent?.tags ?? [], ...this.ownTags];
|
|
83
|
+
}
|
|
84
|
+
ownDescription;
|
|
85
|
+
ownTechnology;
|
|
86
|
+
ownTags;
|
|
87
|
+
description;
|
|
88
|
+
technology;
|
|
89
|
+
tags;
|
|
90
|
+
};
|
|
91
|
+
function mergeArchetypeWithOverride(archetype, override) {
|
|
92
|
+
return {
|
|
93
|
+
description: override?.description ?? archetype.description,
|
|
94
|
+
technology: override?.technology ?? archetype.technology,
|
|
95
|
+
tags: [...archetype.tags, ...override?.tags ?? []]
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// node_modules/.pnpm/change-case@5.4.4/node_modules/change-case/dist/index.js
|
|
100
|
+
var SPLIT_LOWER_UPPER_RE = /([\p{Ll}\d])(\p{Lu})/gu;
|
|
101
|
+
var SPLIT_UPPER_UPPER_RE = /(\p{Lu})([\p{Lu}][\p{Ll}])/gu;
|
|
102
|
+
var SPLIT_SEPARATE_NUMBER_RE = /(\d)\p{Ll}|(\p{L})\d/u;
|
|
103
|
+
var DEFAULT_STRIP_REGEXP = /[^\p{L}\d]+/giu;
|
|
104
|
+
var SPLIT_REPLACE_VALUE = "$1\0$2";
|
|
105
|
+
var DEFAULT_PREFIX_SUFFIX_CHARACTERS = "";
|
|
106
|
+
function split(value) {
|
|
107
|
+
let result = value.trim();
|
|
108
|
+
result = result.replace(SPLIT_LOWER_UPPER_RE, SPLIT_REPLACE_VALUE).replace(SPLIT_UPPER_UPPER_RE, SPLIT_REPLACE_VALUE);
|
|
109
|
+
result = result.replace(DEFAULT_STRIP_REGEXP, "\0");
|
|
110
|
+
let start = 0;
|
|
111
|
+
let end = result.length;
|
|
112
|
+
while (result.charAt(start) === "\0")
|
|
113
|
+
start++;
|
|
114
|
+
if (start === end)
|
|
115
|
+
return [];
|
|
116
|
+
while (result.charAt(end - 1) === "\0")
|
|
117
|
+
end--;
|
|
118
|
+
return result.slice(start, end).split(/\0/g);
|
|
119
|
+
}
|
|
120
|
+
function splitSeparateNumbers(value) {
|
|
121
|
+
const words = split(value);
|
|
122
|
+
for (let i = 0; i < words.length; i++) {
|
|
123
|
+
const word = words[i];
|
|
124
|
+
const match = SPLIT_SEPARATE_NUMBER_RE.exec(word);
|
|
125
|
+
if (match) {
|
|
126
|
+
const offset = match.index + (match[1] ?? match[2]).length;
|
|
127
|
+
words.splice(i, 1, word.slice(0, offset), word.slice(offset));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return words;
|
|
131
|
+
}
|
|
132
|
+
function camelCase(input, options) {
|
|
133
|
+
const [prefix, words, suffix] = splitPrefixSuffix(input, options);
|
|
134
|
+
const lower = lowerFactory(options?.locale);
|
|
135
|
+
const upper = upperFactory(options?.locale);
|
|
136
|
+
const transform = options?.mergeAmbiguousCharacters ? capitalCaseTransformFactory(lower, upper) : pascalCaseTransformFactory(lower, upper);
|
|
137
|
+
return prefix + words.map((word, index) => {
|
|
138
|
+
if (index === 0)
|
|
139
|
+
return lower(word);
|
|
140
|
+
return transform(word, index);
|
|
141
|
+
}).join(options?.delimiter ?? "") + suffix;
|
|
142
|
+
}
|
|
143
|
+
function lowerFactory(locale) {
|
|
144
|
+
return locale === false ? (input) => input.toLowerCase() : (input) => input.toLocaleLowerCase(locale);
|
|
145
|
+
}
|
|
146
|
+
function upperFactory(locale) {
|
|
147
|
+
return locale === false ? (input) => input.toUpperCase() : (input) => input.toLocaleUpperCase(locale);
|
|
148
|
+
}
|
|
149
|
+
function capitalCaseTransformFactory(lower, upper) {
|
|
150
|
+
return (word) => `${upper(word[0])}${lower(word.slice(1))}`;
|
|
151
|
+
}
|
|
152
|
+
function pascalCaseTransformFactory(lower, upper) {
|
|
153
|
+
return (word, index) => {
|
|
154
|
+
const char0 = word[0];
|
|
155
|
+
const initial = index > 0 && char0 >= "0" && char0 <= "9" ? "_" + char0 : upper(char0);
|
|
156
|
+
return initial + lower(word.slice(1));
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function splitPrefixSuffix(input, options = {}) {
|
|
160
|
+
const splitFn = options.split ?? (options.separateNumbers ? splitSeparateNumbers : split);
|
|
161
|
+
const prefixCharacters = options.prefixCharacters ?? DEFAULT_PREFIX_SUFFIX_CHARACTERS;
|
|
162
|
+
const suffixCharacters = options.suffixCharacters ?? DEFAULT_PREFIX_SUFFIX_CHARACTERS;
|
|
163
|
+
let prefixIndex = 0;
|
|
164
|
+
let suffixIndex = input.length;
|
|
165
|
+
while (prefixIndex < input.length) {
|
|
166
|
+
const char = input.charAt(prefixIndex);
|
|
167
|
+
if (!prefixCharacters.includes(char))
|
|
168
|
+
break;
|
|
169
|
+
prefixIndex++;
|
|
170
|
+
}
|
|
171
|
+
while (suffixIndex > prefixIndex) {
|
|
172
|
+
const index = suffixIndex - 1;
|
|
173
|
+
const char = input.charAt(index);
|
|
174
|
+
if (!suffixCharacters.includes(char))
|
|
175
|
+
break;
|
|
176
|
+
suffixIndex = index;
|
|
177
|
+
}
|
|
178
|
+
return [
|
|
179
|
+
input.slice(0, prefixIndex),
|
|
180
|
+
splitFn(input.slice(prefixIndex, suffixIndex)),
|
|
181
|
+
input.slice(suffixIndex)
|
|
182
|
+
];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// libs/c4-model/src/core.ts
|
|
186
|
+
var Element = class {
|
|
187
|
+
constructor(name, defaultTags = [], definition, archetype, overrideDefinition) {
|
|
188
|
+
this.name = name;
|
|
189
|
+
this.description = definition?.description;
|
|
190
|
+
this.tags = (definition?.tags ?? []).concat(["Element"]).concat(defaultTags);
|
|
191
|
+
this.archetype = archetype;
|
|
192
|
+
this.overrideDefinition = overrideDefinition;
|
|
193
|
+
}
|
|
194
|
+
description;
|
|
195
|
+
tags;
|
|
196
|
+
archetype;
|
|
197
|
+
overrideDefinition;
|
|
198
|
+
_relationships = [];
|
|
199
|
+
get canonicalName() {
|
|
200
|
+
return camelCase(this.name);
|
|
201
|
+
}
|
|
202
|
+
uses(otherElement, archetypeOrDef, override) {
|
|
203
|
+
let definition;
|
|
204
|
+
let archetype;
|
|
205
|
+
if (archetypeOrDef instanceof RelationshipArchetype) {
|
|
206
|
+
archetype = archetypeOrDef;
|
|
207
|
+
definition = mergeArchetypeWithOverride(archetypeOrDef, override);
|
|
208
|
+
} else {
|
|
209
|
+
definition = archetypeOrDef;
|
|
210
|
+
}
|
|
211
|
+
const relationship = new Relationship(this, otherElement, definition, archetype, override);
|
|
212
|
+
this._relationships.push(relationship);
|
|
213
|
+
}
|
|
214
|
+
get relationships() {
|
|
215
|
+
return this._relationships;
|
|
216
|
+
}
|
|
217
|
+
getRelationshipsInHierarchy() {
|
|
218
|
+
return this._relationships.concat(this.getChildElements().flatMap((element) => element.getRelationshipsInHierarchy()));
|
|
219
|
+
}
|
|
220
|
+
getChildElementNames(path2) {
|
|
221
|
+
const result = Array.from(this.getChildElements()).flatMap((reference) => {
|
|
222
|
+
const currentPath = `${path2 ? path2 : "" + this.name}.${reference.name}`;
|
|
223
|
+
return [currentPath, ...reference.getChildElementNames(currentPath)];
|
|
224
|
+
});
|
|
225
|
+
return result;
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
var TechnicalElement = class extends Element {
|
|
229
|
+
technology;
|
|
230
|
+
constructor(name, defaultTags = [], definition, archetype, overrideDefinition) {
|
|
231
|
+
super(name, defaultTags, definition, archetype, overrideDefinition);
|
|
232
|
+
this.technology = definition?.technology;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
var Relationship = class {
|
|
236
|
+
constructor(source, destination, definition, archetype, overrideDefinition) {
|
|
237
|
+
this.source = source;
|
|
238
|
+
this.destination = destination;
|
|
239
|
+
this.description = definition?.description;
|
|
240
|
+
this.technology = definition?.technology;
|
|
241
|
+
this.tags = (definition?.tags ?? []).concat(["Relationship"]);
|
|
242
|
+
this.archetype = archetype;
|
|
243
|
+
this.overrideDefinition = overrideDefinition;
|
|
244
|
+
}
|
|
245
|
+
description;
|
|
246
|
+
tags;
|
|
247
|
+
technology;
|
|
248
|
+
archetype;
|
|
249
|
+
overrideDefinition;
|
|
250
|
+
};
|
|
251
|
+
var Group = class {
|
|
252
|
+
constructor(name) {
|
|
253
|
+
this.name = name;
|
|
254
|
+
}
|
|
255
|
+
// TODO: Implement this in some useful way?
|
|
256
|
+
// public addToGroup(groupCollection: string, groupMember: T) {}
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// libs/c4-model/src/component.ts
|
|
260
|
+
var Component = class extends TechnicalElement {
|
|
261
|
+
constructor(name, definition, archetype, overrideDefinition) {
|
|
262
|
+
super(name, ["Component"], definition, archetype, overrideDefinition);
|
|
263
|
+
this.name = name;
|
|
264
|
+
}
|
|
265
|
+
getChildElements() {
|
|
266
|
+
return [];
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
// libs/c4-model/src/container.ts
|
|
271
|
+
var ContainerGroup = class extends Group {
|
|
272
|
+
constructor(name, container) {
|
|
273
|
+
super(name);
|
|
274
|
+
this.name = name;
|
|
275
|
+
this.container = container;
|
|
276
|
+
}
|
|
277
|
+
_components = /* @__PURE__ */ new Map();
|
|
278
|
+
defineComponent(name, archetypeOrDef, override) {
|
|
279
|
+
const component = this.container.defineComponent(name, archetypeOrDef, override);
|
|
280
|
+
this._components.set(name, component);
|
|
281
|
+
return component;
|
|
282
|
+
}
|
|
283
|
+
getComponents() {
|
|
284
|
+
return Array.from(this._components.values());
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
var Container = class extends TechnicalElement {
|
|
288
|
+
constructor(name, definition, archetype, overrideDefinition) {
|
|
289
|
+
super(name, ["Container"], definition, archetype, overrideDefinition);
|
|
290
|
+
this.name = name;
|
|
291
|
+
}
|
|
292
|
+
_components = /* @__PURE__ */ new Map();
|
|
293
|
+
_groups = /* @__PURE__ */ new Map();
|
|
294
|
+
defineComponent(name, archetypeOrDef, override) {
|
|
295
|
+
if (this._components.has(name)) {
|
|
296
|
+
throw Error(`A Component named '${name}' is defined elsewhere in this Container. A Component can be defined only once.`);
|
|
297
|
+
}
|
|
298
|
+
let definition;
|
|
299
|
+
let archetype;
|
|
300
|
+
if (archetypeOrDef instanceof ElementArchetype) {
|
|
301
|
+
archetype = archetypeOrDef;
|
|
302
|
+
definition = mergeArchetypeWithOverride(archetypeOrDef, override);
|
|
303
|
+
} else {
|
|
304
|
+
definition = archetypeOrDef;
|
|
305
|
+
}
|
|
306
|
+
const component = new Component(name, definition, archetype, override);
|
|
307
|
+
this._components.set(name, component);
|
|
308
|
+
return component;
|
|
309
|
+
}
|
|
310
|
+
addGroup(groupName) {
|
|
311
|
+
let group = this._groups.get(groupName);
|
|
312
|
+
if (!group) {
|
|
313
|
+
group = new ContainerGroup(groupName, this);
|
|
314
|
+
this._groups.set(groupName, group);
|
|
315
|
+
}
|
|
316
|
+
return group;
|
|
317
|
+
}
|
|
318
|
+
getGroups() {
|
|
319
|
+
return Array.from(this._groups.values());
|
|
320
|
+
}
|
|
321
|
+
getComponentsNotInGroups() {
|
|
322
|
+
const componentsInGroups = this.getGroups().flatMap((group) => group.getComponents());
|
|
323
|
+
return Array.from(this._components.values()).filter((component) => !componentsInGroups.includes(component));
|
|
324
|
+
}
|
|
325
|
+
getChildElements() {
|
|
326
|
+
return Array.from(this._components.values());
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
// libs/c4-model/src/softwareSystem.ts
|
|
331
|
+
var SoftwareSystemGroup = class extends Group {
|
|
332
|
+
constructor(name, softwareSystem) {
|
|
333
|
+
super(name);
|
|
334
|
+
this.name = name;
|
|
335
|
+
this.softwareSystem = softwareSystem;
|
|
336
|
+
}
|
|
337
|
+
_containers = /* @__PURE__ */ new Map();
|
|
338
|
+
defineContainer(name, archetypeOrDef, override) {
|
|
339
|
+
const container = this.softwareSystem.defineContainer(name, archetypeOrDef, override);
|
|
340
|
+
this._containers.set(name, container);
|
|
341
|
+
return container;
|
|
342
|
+
}
|
|
343
|
+
getContainers() {
|
|
344
|
+
return Array.from(this._containers.values());
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
var SoftwareSystem = class extends Element {
|
|
348
|
+
constructor(name, definition, archetype, overrideDefinition) {
|
|
349
|
+
super(name, ["Software System"], definition, archetype, overrideDefinition);
|
|
350
|
+
this.name = name;
|
|
351
|
+
}
|
|
352
|
+
_containers = /* @__PURE__ */ new Map();
|
|
353
|
+
_groups = /* @__PURE__ */ new Map();
|
|
354
|
+
defineContainer(name, archetypeOrDef, override) {
|
|
355
|
+
if (this._containers.has(name)) {
|
|
356
|
+
throw Error(`A Container named '${name}' is defined elsewhere in this SoftwareSystem. A Container can be defined only once.`);
|
|
357
|
+
}
|
|
358
|
+
let definition;
|
|
359
|
+
let archetype;
|
|
360
|
+
if (archetypeOrDef instanceof ElementArchetype) {
|
|
361
|
+
archetype = archetypeOrDef;
|
|
362
|
+
definition = mergeArchetypeWithOverride(archetypeOrDef, override);
|
|
363
|
+
} else {
|
|
364
|
+
definition = archetypeOrDef;
|
|
365
|
+
}
|
|
366
|
+
const container = new Container(name, definition, archetype, override);
|
|
367
|
+
this._containers.set(name, container);
|
|
368
|
+
return container;
|
|
369
|
+
}
|
|
370
|
+
addGroup(groupName) {
|
|
371
|
+
let group = this._groups.get(groupName);
|
|
372
|
+
if (!group) {
|
|
373
|
+
group = new SoftwareSystemGroup(groupName, this);
|
|
374
|
+
this._groups.set(groupName, group);
|
|
375
|
+
}
|
|
376
|
+
return group;
|
|
377
|
+
}
|
|
378
|
+
getGroups() {
|
|
379
|
+
return Array.from(this._groups.values());
|
|
380
|
+
}
|
|
381
|
+
getChildElements() {
|
|
382
|
+
return Array.from(this._containers.values());
|
|
383
|
+
}
|
|
384
|
+
getContainersNotInGroups() {
|
|
385
|
+
const containersInGroups = Array.from(this._groups.values()).flatMap((group) => group.getContainers());
|
|
386
|
+
return Array.from(this._containers.values()).filter((container) => !containersInGroups.includes(container));
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
// libs/c4-model/src/person.ts
|
|
391
|
+
var Person = class extends Element {
|
|
392
|
+
constructor(name, definition, archetype, overrideDefinition) {
|
|
393
|
+
super(name, ["Person"], definition, archetype, overrideDefinition);
|
|
394
|
+
this.name = name;
|
|
395
|
+
}
|
|
396
|
+
getChildElements() {
|
|
397
|
+
return [];
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
// libs/c4-model/src/model.ts
|
|
402
|
+
var ModelGroup = class extends Group {
|
|
403
|
+
constructor(name, model) {
|
|
404
|
+
super(name);
|
|
405
|
+
this.name = name;
|
|
406
|
+
this.model = model;
|
|
407
|
+
}
|
|
408
|
+
softwareSystems = /* @__PURE__ */ new Map();
|
|
409
|
+
people = /* @__PURE__ */ new Map();
|
|
410
|
+
defineSoftwareSystem(name, archetypeOrDef, override) {
|
|
411
|
+
const softwareSystem = this.model.defineSoftwareSystem(name, archetypeOrDef, override);
|
|
412
|
+
this.softwareSystems.set(name, softwareSystem);
|
|
413
|
+
return softwareSystem;
|
|
414
|
+
}
|
|
415
|
+
definePerson(name, archetypeOrDef, override) {
|
|
416
|
+
const person = this.model.definePerson(name, archetypeOrDef, override);
|
|
417
|
+
this.people.set(name, person);
|
|
418
|
+
return person;
|
|
419
|
+
}
|
|
420
|
+
getSoftwareSystems() {
|
|
421
|
+
return Array.from(this.softwareSystems.values());
|
|
422
|
+
}
|
|
423
|
+
getPeople() {
|
|
424
|
+
return Array.from(this.people.values());
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
var Model = class {
|
|
428
|
+
constructor(name) {
|
|
429
|
+
this.name = name;
|
|
430
|
+
}
|
|
431
|
+
softwareSystems = /* @__PURE__ */ new Map();
|
|
432
|
+
people = /* @__PURE__ */ new Map();
|
|
433
|
+
groups = /* @__PURE__ */ new Map();
|
|
434
|
+
defineSoftwareSystem(name, archetypeOrDef, override) {
|
|
435
|
+
if (this.softwareSystems.has(name)) {
|
|
436
|
+
throw Error(`A SoftwareSystem named '${name}' is defined elsewhere in this Model. A SoftwareSystem can be defined only once.`);
|
|
437
|
+
}
|
|
438
|
+
let definition;
|
|
439
|
+
let archetype;
|
|
440
|
+
if (archetypeOrDef instanceof ElementArchetype) {
|
|
441
|
+
archetype = archetypeOrDef;
|
|
442
|
+
definition = mergeArchetypeWithOverride(archetypeOrDef, override);
|
|
443
|
+
} else {
|
|
444
|
+
definition = archetypeOrDef;
|
|
445
|
+
}
|
|
446
|
+
const system = new SoftwareSystem(name, definition, archetype, override);
|
|
447
|
+
this.softwareSystems.set(name, system);
|
|
448
|
+
return system;
|
|
449
|
+
}
|
|
450
|
+
// TODO:Should be a Group<SoftwareSystem | Person> if that is added back in
|
|
451
|
+
addGroup(groupName) {
|
|
452
|
+
let group = this.groups.get(groupName);
|
|
453
|
+
if (!group) {
|
|
454
|
+
group = new ModelGroup(groupName, this);
|
|
455
|
+
this.groups.set(groupName, group);
|
|
456
|
+
}
|
|
457
|
+
return group;
|
|
458
|
+
}
|
|
459
|
+
definePerson(name, archetypeOrDef, override) {
|
|
460
|
+
if (this.people.has(name)) {
|
|
461
|
+
throw Error(`A Person named '${name}' is defined elsewhere in this Model. A Person can be defined only once.`);
|
|
462
|
+
}
|
|
463
|
+
let definition;
|
|
464
|
+
let archetype;
|
|
465
|
+
if (archetypeOrDef instanceof ElementArchetype) {
|
|
466
|
+
archetype = archetypeOrDef;
|
|
467
|
+
definition = mergeArchetypeWithOverride(archetypeOrDef, override);
|
|
468
|
+
} else {
|
|
469
|
+
definition = archetypeOrDef;
|
|
470
|
+
}
|
|
471
|
+
const person = new Person(name, definition, archetype, override);
|
|
472
|
+
this.people.set(name, person);
|
|
473
|
+
return person;
|
|
474
|
+
}
|
|
475
|
+
validate() {
|
|
476
|
+
}
|
|
477
|
+
getPeople() {
|
|
478
|
+
return Array.from(this.people.values());
|
|
479
|
+
}
|
|
480
|
+
getSoftwareSystems() {
|
|
481
|
+
return Array.from(this.softwareSystems.values());
|
|
482
|
+
}
|
|
483
|
+
getPeopleNotInGroups() {
|
|
484
|
+
const peopleInGroups = Array.from(this.groups.values()).flatMap((group) => group.getPeople());
|
|
485
|
+
return Array.from(this.people.values()).filter((person) => !peopleInGroups.includes(person));
|
|
486
|
+
}
|
|
487
|
+
getSoftwareSystemsNotInGroups() {
|
|
488
|
+
const systemsInGroups = Array.from(this.groups.values()).flatMap((group) => group.getSoftwareSystems());
|
|
489
|
+
return Array.from(this.softwareSystems.values()).filter((system) => !systemsInGroups.includes(system));
|
|
490
|
+
}
|
|
491
|
+
getGroups() {
|
|
492
|
+
return Array.from(this.groups.values());
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
// libs/c4-model/src/views.ts
|
|
497
|
+
var View = class {
|
|
498
|
+
constructor(key, viewDefinition) {
|
|
499
|
+
this.key = key;
|
|
500
|
+
this.description = viewDefinition.description;
|
|
501
|
+
this.subject = viewDefinition.subject;
|
|
502
|
+
this.title = viewDefinition.title;
|
|
503
|
+
}
|
|
504
|
+
subject;
|
|
505
|
+
description;
|
|
506
|
+
title;
|
|
507
|
+
_scopes = [];
|
|
508
|
+
includeAll() {
|
|
509
|
+
this._scopes.push("include *");
|
|
510
|
+
}
|
|
511
|
+
includeElement(element) {
|
|
512
|
+
this._scopes.push(`include ${element.canonicalName}`);
|
|
513
|
+
}
|
|
514
|
+
includeExpression(expression) {
|
|
515
|
+
this._scopes.push(`include ${expression}`);
|
|
516
|
+
}
|
|
517
|
+
excludeAll() {
|
|
518
|
+
this._scopes.push("exclude *");
|
|
519
|
+
}
|
|
520
|
+
excludeElement(element) {
|
|
521
|
+
this._scopes.push(`exclude ${element.canonicalName}`);
|
|
522
|
+
}
|
|
523
|
+
excludeExpression(expression) {
|
|
524
|
+
this._scopes.push(`exclude ${expression}`);
|
|
525
|
+
}
|
|
526
|
+
get scopes() {
|
|
527
|
+
return this._scopes;
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
var Views = class {
|
|
531
|
+
_systemLandscapeViews = /* @__PURE__ */ new Map();
|
|
532
|
+
_systemContextViews = /* @__PURE__ */ new Map();
|
|
533
|
+
_containerViews = /* @__PURE__ */ new Map();
|
|
534
|
+
_componentViews = /* @__PURE__ */ new Map();
|
|
535
|
+
addSystemLandscapeView(key, definition) {
|
|
536
|
+
const view = new View(key, { subject: void 0, description: definition.description, title: definition.title });
|
|
537
|
+
this._systemLandscapeViews.set(key, view);
|
|
538
|
+
return view;
|
|
539
|
+
}
|
|
540
|
+
addSystemContextView(key, definition) {
|
|
541
|
+
const view = new View(key, definition);
|
|
542
|
+
this._systemContextViews.set(key, view);
|
|
543
|
+
return view;
|
|
544
|
+
}
|
|
545
|
+
addContainerView(key, definition) {
|
|
546
|
+
const view = new View(key, definition);
|
|
547
|
+
this._containerViews.set(key, view);
|
|
548
|
+
return view;
|
|
549
|
+
}
|
|
550
|
+
addComponentView(key, definition) {
|
|
551
|
+
const view = new View(key, definition);
|
|
552
|
+
this._componentViews.set(key, view);
|
|
553
|
+
return view;
|
|
554
|
+
}
|
|
555
|
+
get systemLandscapeViews() {
|
|
556
|
+
return Array.from(this._systemLandscapeViews.values());
|
|
557
|
+
}
|
|
558
|
+
get systemContextViews() {
|
|
559
|
+
return Array.from(this._systemContextViews.values());
|
|
560
|
+
}
|
|
561
|
+
get containerViews() {
|
|
562
|
+
return Array.from(this._containerViews.values());
|
|
563
|
+
}
|
|
564
|
+
get componentViews() {
|
|
565
|
+
return Array.from(this._componentViews.values());
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
// libs/c4-model/src/structurizrDslWriter.ts
|
|
570
|
+
var INDENT_SIZE = 2;
|
|
571
|
+
var StructurizrDSLWriter = class {
|
|
572
|
+
constructor(model, views) {
|
|
573
|
+
this.model = model;
|
|
574
|
+
this.views = views;
|
|
575
|
+
}
|
|
576
|
+
collectArchetypes() {
|
|
577
|
+
const elementSet = /* @__PURE__ */ new Set();
|
|
578
|
+
const relationshipSet = /* @__PURE__ */ new Set();
|
|
579
|
+
const collectFromElement = (element) => {
|
|
580
|
+
if (element.archetype) {
|
|
581
|
+
let arch = element.archetype;
|
|
582
|
+
while (arch) {
|
|
583
|
+
elementSet.add(arch);
|
|
584
|
+
arch = arch.parent;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
element.relationships.forEach((rel) => {
|
|
588
|
+
if (rel.archetype) {
|
|
589
|
+
let arch = rel.archetype;
|
|
590
|
+
while (arch) {
|
|
591
|
+
relationshipSet.add(arch);
|
|
592
|
+
arch = arch.parent;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
element.getChildElements().forEach(collectFromElement);
|
|
597
|
+
};
|
|
598
|
+
const allElements = [...this.model.getPeople(), ...this.model.getSoftwareSystems()];
|
|
599
|
+
allElements.forEach(collectFromElement);
|
|
600
|
+
const sortArchetypes = (set) => {
|
|
601
|
+
const sorted = [];
|
|
602
|
+
const visited = /* @__PURE__ */ new Set();
|
|
603
|
+
const visit = (arch) => {
|
|
604
|
+
if (visited.has(arch)) return;
|
|
605
|
+
if (arch.parent && set.has(arch.parent)) visit(arch.parent);
|
|
606
|
+
visited.add(arch);
|
|
607
|
+
sorted.push(arch);
|
|
608
|
+
};
|
|
609
|
+
set.forEach(visit);
|
|
610
|
+
return sorted;
|
|
611
|
+
};
|
|
612
|
+
return {
|
|
613
|
+
elementArchetypes: sortArchetypes(elementSet),
|
|
614
|
+
relationshipArchetypes: sortArchetypes(relationshipSet)
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
writeArchetypes(level) {
|
|
618
|
+
const { elementArchetypes, relationshipArchetypes } = this.collectArchetypes();
|
|
619
|
+
if (elementArchetypes.length === 0 && relationshipArchetypes.length === 0) return "";
|
|
620
|
+
let dsl = this.writeLine(`archetypes {`, level);
|
|
621
|
+
for (const arch of elementArchetypes) {
|
|
622
|
+
const baseType = arch.parent ? arch.parent.name : arch.elementKind;
|
|
623
|
+
let inner = "";
|
|
624
|
+
if (arch.ownDescription) {
|
|
625
|
+
inner += this.writeLine(`description "${arch.ownDescription}"`, level + 2);
|
|
626
|
+
}
|
|
627
|
+
if (arch.ownTechnology) {
|
|
628
|
+
inner += this.writeLine(`technology "${arch.ownTechnology}"`, level + 2);
|
|
629
|
+
}
|
|
630
|
+
if (arch.ownTags.length > 0) {
|
|
631
|
+
inner += this.writeLine(`tags ${arch.ownTags.map((t) => `"${t}"`).join(" ")}`, level + 2);
|
|
632
|
+
}
|
|
633
|
+
if (inner) {
|
|
634
|
+
dsl += this.writeLine(`${arch.name} = ${baseType} {`, level + 1);
|
|
635
|
+
dsl += inner;
|
|
636
|
+
dsl += this.writeLine(`}`, level + 1);
|
|
637
|
+
} else {
|
|
638
|
+
dsl += this.writeLine(`${arch.name} = ${baseType} {}`, level + 1);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
for (const arch of relationshipArchetypes) {
|
|
642
|
+
const arrow = arch.parent ? `--${arch.parent.name}->` : `->`;
|
|
643
|
+
let inner = "";
|
|
644
|
+
if (arch.ownDescription) {
|
|
645
|
+
inner += this.writeLine(`description "${arch.ownDescription}"`, level + 2);
|
|
646
|
+
}
|
|
647
|
+
if (arch.ownTechnology) {
|
|
648
|
+
inner += this.writeLine(`technology "${arch.ownTechnology}"`, level + 2);
|
|
649
|
+
}
|
|
650
|
+
if (arch.ownTags.length > 0) {
|
|
651
|
+
inner += this.writeLine(`tags ${arch.ownTags.map((t) => `"${t}"`).join(" ")}`, level + 2);
|
|
652
|
+
}
|
|
653
|
+
if (inner) {
|
|
654
|
+
dsl += this.writeLine(`${arch.name} = ${arrow} {`, level + 1);
|
|
655
|
+
dsl += inner;
|
|
656
|
+
dsl += this.writeLine(`}`, level + 1);
|
|
657
|
+
} else {
|
|
658
|
+
dsl += this.writeLine(`${arch.name} = ${arrow} {}`, level + 1);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
dsl += this.writeLine(`}`, level);
|
|
662
|
+
return dsl;
|
|
663
|
+
}
|
|
664
|
+
writeElement(elementType, element, level, closeElement = true) {
|
|
665
|
+
let elementDsl = "";
|
|
666
|
+
const type = element.archetype ? element.archetype.name : elementType;
|
|
667
|
+
elementDsl += this.writeLine(`${element.canonicalName} = ${type} "${element.name}" {`, level);
|
|
668
|
+
if (element.archetype) {
|
|
669
|
+
const ovr = element.overrideDefinition;
|
|
670
|
+
if (ovr?.description) {
|
|
671
|
+
elementDsl += this.writeLine(`description "${ovr.description}"`, level + 1);
|
|
672
|
+
}
|
|
673
|
+
if (ovr?.tags && ovr.tags.length > 0) {
|
|
674
|
+
elementDsl += this.writeLine(`tags ${ovr.tags.map((tag) => `"${tag}"`).join(" ")}`, level + 1);
|
|
675
|
+
}
|
|
676
|
+
if (ovr && "technology" in ovr && ovr.technology) {
|
|
677
|
+
elementDsl += this.writeLine(`technology "${ovr.technology}"`, level + 1);
|
|
678
|
+
}
|
|
679
|
+
} else {
|
|
680
|
+
if (element.description) {
|
|
681
|
+
elementDsl += this.writeLine(`description "${element.description}"`, level + 1);
|
|
682
|
+
}
|
|
683
|
+
elementDsl += this.writeLine(`tags ${element.tags.map((tag) => `"${tag}"`).join(" ")}`, level + 1);
|
|
684
|
+
}
|
|
685
|
+
if (closeElement) {
|
|
686
|
+
elementDsl += this.writeLine(`}`, level);
|
|
687
|
+
}
|
|
688
|
+
return elementDsl;
|
|
689
|
+
}
|
|
690
|
+
writeComponent(component, level) {
|
|
691
|
+
let componentDsl = "";
|
|
692
|
+
componentDsl += this.writeElement("component", component, level, false);
|
|
693
|
+
if (!component.archetype && component.technology) {
|
|
694
|
+
componentDsl += this.writeLine(`technology "${component.technology}"`, level + 1);
|
|
695
|
+
}
|
|
696
|
+
componentDsl += this.writeLine(`}`, level);
|
|
697
|
+
return componentDsl;
|
|
698
|
+
}
|
|
699
|
+
writeContainerGroup(group, level) {
|
|
700
|
+
let containerGroupDsl = "";
|
|
701
|
+
containerGroupDsl += this.writeLine(`${group.name} = group "${group.name}" {`, level);
|
|
702
|
+
group.getComponents().forEach((component) => {
|
|
703
|
+
containerGroupDsl += this.writeComponent(component, level + 1);
|
|
704
|
+
});
|
|
705
|
+
containerGroupDsl += this.writeLine(`}`, level);
|
|
706
|
+
return containerGroupDsl;
|
|
707
|
+
}
|
|
708
|
+
writeContainer(container, level) {
|
|
709
|
+
let containerDsl = "";
|
|
710
|
+
containerDsl += this.writeElement("container", container, level, false);
|
|
711
|
+
if (!container.archetype && container.technology) {
|
|
712
|
+
containerDsl += this.writeLine(`technology "${container.technology}"`, level + 1);
|
|
713
|
+
}
|
|
714
|
+
container.getComponentsNotInGroups().forEach((component) => {
|
|
715
|
+
containerDsl += this.writeComponent(component, level + 1);
|
|
716
|
+
});
|
|
717
|
+
container.getGroups().forEach((group) => {
|
|
718
|
+
containerDsl += this.writeContainerGroup(group, level + 1);
|
|
719
|
+
});
|
|
720
|
+
containerDsl += this.writeLine(`}`, level);
|
|
721
|
+
return containerDsl;
|
|
722
|
+
}
|
|
723
|
+
writeSoftwareSystemGroup(group, level) {
|
|
724
|
+
let softwareSystemGroupDsl = "";
|
|
725
|
+
softwareSystemGroupDsl += this.writeLine(`${group.name} = group "${group.name}" {`, level);
|
|
726
|
+
group.getContainers().forEach((container) => {
|
|
727
|
+
softwareSystemGroupDsl += this.writeContainer(container, level + 1);
|
|
728
|
+
});
|
|
729
|
+
softwareSystemGroupDsl += this.writeLine(`}`, level);
|
|
730
|
+
return softwareSystemGroupDsl;
|
|
731
|
+
}
|
|
732
|
+
writeSoftwareSystem(softwareSystem, level) {
|
|
733
|
+
let softwareSystemDsl = "";
|
|
734
|
+
softwareSystemDsl += this.writeElement("softwareSystem", softwareSystem, level, false);
|
|
735
|
+
softwareSystem.getContainersNotInGroups().forEach((container) => {
|
|
736
|
+
softwareSystemDsl += this.writeContainer(container, level + 1);
|
|
737
|
+
});
|
|
738
|
+
softwareSystem.getGroups().forEach((group) => {
|
|
739
|
+
softwareSystemDsl += this.writeSoftwareSystemGroup(group, level + 1);
|
|
740
|
+
});
|
|
741
|
+
softwareSystemDsl += this.writeLine(`}`, level);
|
|
742
|
+
return softwareSystemDsl;
|
|
743
|
+
}
|
|
744
|
+
writeRelationship(relationship, level) {
|
|
745
|
+
let dsl = "";
|
|
746
|
+
if (relationship.archetype) {
|
|
747
|
+
const arrow = `--${relationship.archetype.name}->`;
|
|
748
|
+
const ovr = relationship.overrideDefinition;
|
|
749
|
+
const desc = ovr?.description ?? relationship.description ?? "uses";
|
|
750
|
+
dsl += this.writeLine(
|
|
751
|
+
`${relationship.source.canonicalName} ${arrow} ${relationship.destination.canonicalName} "${desc}" {`,
|
|
752
|
+
level
|
|
753
|
+
);
|
|
754
|
+
if (ovr?.technology) {
|
|
755
|
+
dsl += this.writeLine(`technology "${ovr.technology}"`, level + 1);
|
|
756
|
+
}
|
|
757
|
+
if (ovr?.tags && ovr.tags.length > 0) {
|
|
758
|
+
dsl += this.writeLine(`tags ${ovr.tags.map((tag) => `"${tag}"`).join(" ")}`, level + 1);
|
|
759
|
+
}
|
|
760
|
+
dsl += this.writeLine(`}`, level);
|
|
761
|
+
} else {
|
|
762
|
+
const tech = relationship.technology ? ` "${relationship.technology}"` : "";
|
|
763
|
+
dsl += this.writeLine(
|
|
764
|
+
`${relationship.source.canonicalName} -> ${relationship.destination.canonicalName} "${relationship.description ?? "uses"}"${tech} {`,
|
|
765
|
+
level
|
|
766
|
+
);
|
|
767
|
+
dsl += this.writeLine(`tags ${relationship.tags.map((tag) => `"${tag}"`).join(" ")}`, level + 1);
|
|
768
|
+
dsl += this.writeLine(`}`, level);
|
|
769
|
+
}
|
|
770
|
+
return dsl;
|
|
771
|
+
}
|
|
772
|
+
writeRelationships(elements, level) {
|
|
773
|
+
let relationshipsDsl = "";
|
|
774
|
+
elements.forEach((element) => {
|
|
775
|
+
element.getRelationshipsInHierarchy().forEach((relationship) => {
|
|
776
|
+
relationshipsDsl += this.writeRelationship(relationship, level);
|
|
777
|
+
});
|
|
778
|
+
});
|
|
779
|
+
return relationshipsDsl;
|
|
780
|
+
}
|
|
781
|
+
writeModelGroup(group, level) {
|
|
782
|
+
let modelGroupDsl = "";
|
|
783
|
+
modelGroupDsl += this.writeLine(`${group.name} = group "${group.name}" {`, level);
|
|
784
|
+
group.getPeople().forEach((person) => {
|
|
785
|
+
modelGroupDsl += this.writeElement("person", person, level + 1);
|
|
786
|
+
});
|
|
787
|
+
group.getSoftwareSystems().forEach((softwareSystem) => {
|
|
788
|
+
modelGroupDsl += this.writeSoftwareSystem(softwareSystem, level + 1);
|
|
789
|
+
});
|
|
790
|
+
modelGroupDsl += this.writeLine(`}`, level);
|
|
791
|
+
return modelGroupDsl;
|
|
792
|
+
}
|
|
793
|
+
writeModel(model, level) {
|
|
794
|
+
let modelDsl = "";
|
|
795
|
+
modelDsl += this.writeLine(`model {`, level);
|
|
796
|
+
modelDsl += this.writeArchetypes(level + 1);
|
|
797
|
+
modelDsl += this.writeLine("// Elements", level + 1);
|
|
798
|
+
model.getPeopleNotInGroups().forEach((person) => {
|
|
799
|
+
modelDsl += this.writeElement("person", person, level + 1);
|
|
800
|
+
});
|
|
801
|
+
model.getSoftwareSystemsNotInGroups().forEach((softwareSystem) => {
|
|
802
|
+
modelDsl += this.writeSoftwareSystem(softwareSystem, level + 1);
|
|
803
|
+
});
|
|
804
|
+
model.getGroups().forEach((group) => {
|
|
805
|
+
modelDsl += this.writeModelGroup(group, level + 1);
|
|
806
|
+
});
|
|
807
|
+
modelDsl += this.writeLine("// Relationships", level + 1);
|
|
808
|
+
modelDsl += this.writeRelationships(model.getPeople().concat(model.getSoftwareSystems()), level + 1);
|
|
809
|
+
modelDsl += this.writeLine(`}`, level);
|
|
810
|
+
return modelDsl;
|
|
811
|
+
}
|
|
812
|
+
writeView(view, viewType, level) {
|
|
813
|
+
let viewDsl = this.writeLine(`${viewType}${view.subject ? ' "' + view.subject.canonicalName + '"' : ""} "${view.key}" {`, level);
|
|
814
|
+
viewDsl += this.writeLine(`description "${view.description}"`, level + 1);
|
|
815
|
+
if (view.title) {
|
|
816
|
+
viewDsl += this.writeLine(`title "${view.title}"`, level + 1);
|
|
817
|
+
}
|
|
818
|
+
view.scopes.forEach((scope) => {
|
|
819
|
+
viewDsl += this.writeLine(`${scope}`, level + 1);
|
|
820
|
+
});
|
|
821
|
+
viewDsl += this.writeLine(`}`, level);
|
|
822
|
+
return viewDsl;
|
|
823
|
+
}
|
|
824
|
+
writeViews(views, level) {
|
|
825
|
+
let viewDsl = "";
|
|
826
|
+
viewDsl += this.writeLine(`views {`, level);
|
|
827
|
+
viewDsl += this.writeLine("// System Landscape Views", level + 1);
|
|
828
|
+
views.systemLandscapeViews.forEach((view) => {
|
|
829
|
+
viewDsl += this.writeView(view, "systemLandscape", level + 1);
|
|
830
|
+
});
|
|
831
|
+
viewDsl += this.writeLine("// System Context Views", level + 1);
|
|
832
|
+
views.systemContextViews.forEach((view) => {
|
|
833
|
+
viewDsl += this.writeView(view, "systemContext", level + 1);
|
|
834
|
+
});
|
|
835
|
+
viewDsl += this.writeLine("// Container Views", level + 1);
|
|
836
|
+
views.containerViews.forEach((view) => {
|
|
837
|
+
viewDsl += this.writeView(view, "container", level + 1);
|
|
838
|
+
});
|
|
839
|
+
viewDsl += this.writeLine("// Component Views", level + 1);
|
|
840
|
+
views.componentViews.forEach((view) => {
|
|
841
|
+
viewDsl += this.writeView(view, "component", level + 1);
|
|
842
|
+
});
|
|
843
|
+
viewDsl += this.writeLine(`theme default`, level + 1);
|
|
844
|
+
viewDsl += this.writeLine(`}`, level);
|
|
845
|
+
return viewDsl;
|
|
846
|
+
}
|
|
847
|
+
write() {
|
|
848
|
+
let dsl = "";
|
|
849
|
+
this.model.validate();
|
|
850
|
+
dsl += this.writeLine(`workspace "${this.model.name}" {`, 0);
|
|
851
|
+
dsl += this.writeModel(this.model, 1);
|
|
852
|
+
dsl += this.writeViews(this.views, 1);
|
|
853
|
+
dsl += this.writeLine(`}`, 0);
|
|
854
|
+
return dsl;
|
|
855
|
+
}
|
|
856
|
+
writeLine(line, level) {
|
|
857
|
+
const indent = " ".repeat(level * INDENT_SIZE);
|
|
858
|
+
return `${indent}${line}
|
|
859
|
+
`;
|
|
860
|
+
}
|
|
861
|
+
};
|
|
862
|
+
|
|
863
|
+
// libs/c4-model/src/buildModel.ts
|
|
864
|
+
var import_glob = require("glob");
|
|
865
|
+
var import_path = require("path");
|
|
866
|
+
var import_url = require("url");
|
|
867
|
+
var import_meta = {};
|
|
868
|
+
var _dirname = typeof __dirname !== "undefined" ? __dirname : (0, import_path.dirname)((0, import_url.fileURLToPath)(import_meta.url));
|
|
869
|
+
async function buildModelWithCatalog(options = {}) {
|
|
870
|
+
const { modelName = "model", modules: explicitModules } = options;
|
|
871
|
+
const model = new Model(modelName);
|
|
872
|
+
let c4Modules;
|
|
873
|
+
if (explicitModules) {
|
|
874
|
+
c4Modules = [...explicitModules];
|
|
875
|
+
} else {
|
|
876
|
+
const { globPath = "c4.dsl.ts", searchRoot = _dirname } = options;
|
|
877
|
+
const result = await (0, import_glob.glob)(`**/${globPath}`, { cwd: searchRoot });
|
|
878
|
+
if (result.length === 0) {
|
|
879
|
+
throw new Error(`No ${globPath} files found`);
|
|
880
|
+
}
|
|
881
|
+
const imported = await Promise.all(result.map((file) => import((0, import_path.join)(searchRoot, file))));
|
|
882
|
+
c4Modules = imported.filter((m) => m.c4Module).map((m) => m.c4Module);
|
|
883
|
+
}
|
|
884
|
+
if (c4Modules.length === 0) {
|
|
885
|
+
throw new Error("No c4Module instances found");
|
|
886
|
+
}
|
|
887
|
+
const registrations = [];
|
|
888
|
+
const rootCatalog = {};
|
|
889
|
+
for (const instance of c4Modules) {
|
|
890
|
+
const local = instance.registerDefinitions(model);
|
|
891
|
+
rootCatalog[instance.key] = local;
|
|
892
|
+
registrations.push({ instance, key: instance.key, local });
|
|
893
|
+
}
|
|
894
|
+
for (const { instance, key, local } of registrations) {
|
|
895
|
+
const dependencies = Object.fromEntries(Object.entries(rootCatalog).filter(([k]) => k !== key));
|
|
896
|
+
instance.buildRelationships(local, dependencies);
|
|
897
|
+
}
|
|
898
|
+
return { model, catalog: rootCatalog };
|
|
899
|
+
}
|
|
900
|
+
async function buildModel(options = {}) {
|
|
901
|
+
return (await buildModelWithCatalog(options)).model;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
// libs/c4-model/src/generateDiagrams.ts
|
|
905
|
+
var fs = __toESM(require("fs"), 1);
|
|
906
|
+
var os = __toESM(require("os"), 1);
|
|
907
|
+
var path = __toESM(require("path"), 1);
|
|
908
|
+
var import_testcontainers = require("testcontainers");
|
|
909
|
+
async function generateDiagrams(options) {
|
|
910
|
+
const { views: viewsFactory, outputDir, ...buildOptions } = options;
|
|
911
|
+
const { model, catalog } = await buildModelWithCatalog(buildOptions);
|
|
912
|
+
const views = viewsFactory(catalog);
|
|
913
|
+
const dsl = new StructurizrDSLWriter(model, views).write();
|
|
914
|
+
const tmpDir = await fs.promises.mkdtemp(path.join(fs.realpathSync(os.tmpdir()), "c4-diagrams-"));
|
|
915
|
+
await fs.promises.writeFile(path.join(tmpDir, "workspace.dsl"), dsl, "utf8");
|
|
916
|
+
await new import_testcontainers.GenericContainer("structurizr/structurizr").withBindMounts([{ source: tmpDir, target: "/workspace", mode: "rw" }]).withCommand(["export", "-w", "/workspace/workspace.dsl", "-f", "mermaid", "-o", "/workspace"]).withWaitStrategy(import_testcontainers.Wait.forOneShotStartup()).start();
|
|
917
|
+
await fs.promises.mkdir(outputDir, { recursive: true });
|
|
918
|
+
const tmpFiles = await fs.promises.readdir(tmpDir);
|
|
919
|
+
const mmdFiles = tmpFiles.filter((f) => f.endsWith(".mmd"));
|
|
920
|
+
for (const file of mmdFiles) {
|
|
921
|
+
await fs.promises.copyFile(path.join(tmpDir, file), path.join(outputDir, file));
|
|
922
|
+
}
|
|
923
|
+
const generatedFiles = mmdFiles.map((f) => path.join(outputDir, f));
|
|
924
|
+
for (const file of mmdFiles) {
|
|
925
|
+
const baseName = path.basename(file, ".mmd");
|
|
926
|
+
const pngFile = `${baseName}.png`;
|
|
927
|
+
await new import_testcontainers.GenericContainer("minlag/mermaid-cli").withBindMounts([{ source: tmpDir, target: "/data", mode: "rw" }]).withCommand(["-i", `/data/${file}`, "-o", `/data/${pngFile}`]).withWaitStrategy(import_testcontainers.Wait.forOneShotStartup()).start();
|
|
928
|
+
await fs.promises.copyFile(path.join(tmpDir, pngFile), path.join(outputDir, pngFile));
|
|
929
|
+
generatedFiles.push(path.join(outputDir, pngFile));
|
|
930
|
+
}
|
|
931
|
+
return generatedFiles;
|
|
932
|
+
}
|
|
933
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
934
|
+
0 && (module.exports = {
|
|
935
|
+
Component,
|
|
936
|
+
Container,
|
|
937
|
+
ContainerGroup,
|
|
938
|
+
ElementArchetype,
|
|
939
|
+
Model,
|
|
940
|
+
ModelGroup,
|
|
941
|
+
Person,
|
|
942
|
+
RelationshipArchetype,
|
|
943
|
+
SoftwareSystem,
|
|
944
|
+
SoftwareSystemGroup,
|
|
945
|
+
StructurizrDSLWriter,
|
|
946
|
+
View,
|
|
947
|
+
Views,
|
|
948
|
+
buildModel,
|
|
949
|
+
buildModelWithCatalog,
|
|
950
|
+
generateDiagrams,
|
|
951
|
+
mergeArchetypeWithOverride
|
|
952
|
+
});
|
package/src/index.js
CHANGED
|
@@ -44,8 +44,93 @@ function mergeArchetypeWithOverride(archetype, override) {
|
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
// node_modules/.pnpm/change-case@5.4.4/node_modules/change-case/dist/index.js
|
|
48
|
+
var SPLIT_LOWER_UPPER_RE = /([\p{Ll}\d])(\p{Lu})/gu;
|
|
49
|
+
var SPLIT_UPPER_UPPER_RE = /(\p{Lu})([\p{Lu}][\p{Ll}])/gu;
|
|
50
|
+
var SPLIT_SEPARATE_NUMBER_RE = /(\d)\p{Ll}|(\p{L})\d/u;
|
|
51
|
+
var DEFAULT_STRIP_REGEXP = /[^\p{L}\d]+/giu;
|
|
52
|
+
var SPLIT_REPLACE_VALUE = "$1\0$2";
|
|
53
|
+
var DEFAULT_PREFIX_SUFFIX_CHARACTERS = "";
|
|
54
|
+
function split(value) {
|
|
55
|
+
let result = value.trim();
|
|
56
|
+
result = result.replace(SPLIT_LOWER_UPPER_RE, SPLIT_REPLACE_VALUE).replace(SPLIT_UPPER_UPPER_RE, SPLIT_REPLACE_VALUE);
|
|
57
|
+
result = result.replace(DEFAULT_STRIP_REGEXP, "\0");
|
|
58
|
+
let start = 0;
|
|
59
|
+
let end = result.length;
|
|
60
|
+
while (result.charAt(start) === "\0")
|
|
61
|
+
start++;
|
|
62
|
+
if (start === end)
|
|
63
|
+
return [];
|
|
64
|
+
while (result.charAt(end - 1) === "\0")
|
|
65
|
+
end--;
|
|
66
|
+
return result.slice(start, end).split(/\0/g);
|
|
67
|
+
}
|
|
68
|
+
function splitSeparateNumbers(value) {
|
|
69
|
+
const words = split(value);
|
|
70
|
+
for (let i = 0; i < words.length; i++) {
|
|
71
|
+
const word = words[i];
|
|
72
|
+
const match = SPLIT_SEPARATE_NUMBER_RE.exec(word);
|
|
73
|
+
if (match) {
|
|
74
|
+
const offset = match.index + (match[1] ?? match[2]).length;
|
|
75
|
+
words.splice(i, 1, word.slice(0, offset), word.slice(offset));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return words;
|
|
79
|
+
}
|
|
80
|
+
function camelCase(input, options) {
|
|
81
|
+
const [prefix, words, suffix] = splitPrefixSuffix(input, options);
|
|
82
|
+
const lower = lowerFactory(options?.locale);
|
|
83
|
+
const upper = upperFactory(options?.locale);
|
|
84
|
+
const transform = options?.mergeAmbiguousCharacters ? capitalCaseTransformFactory(lower, upper) : pascalCaseTransformFactory(lower, upper);
|
|
85
|
+
return prefix + words.map((word, index) => {
|
|
86
|
+
if (index === 0)
|
|
87
|
+
return lower(word);
|
|
88
|
+
return transform(word, index);
|
|
89
|
+
}).join(options?.delimiter ?? "") + suffix;
|
|
90
|
+
}
|
|
91
|
+
function lowerFactory(locale) {
|
|
92
|
+
return locale === false ? (input) => input.toLowerCase() : (input) => input.toLocaleLowerCase(locale);
|
|
93
|
+
}
|
|
94
|
+
function upperFactory(locale) {
|
|
95
|
+
return locale === false ? (input) => input.toUpperCase() : (input) => input.toLocaleUpperCase(locale);
|
|
96
|
+
}
|
|
97
|
+
function capitalCaseTransformFactory(lower, upper) {
|
|
98
|
+
return (word) => `${upper(word[0])}${lower(word.slice(1))}`;
|
|
99
|
+
}
|
|
100
|
+
function pascalCaseTransformFactory(lower, upper) {
|
|
101
|
+
return (word, index) => {
|
|
102
|
+
const char0 = word[0];
|
|
103
|
+
const initial = index > 0 && char0 >= "0" && char0 <= "9" ? "_" + char0 : upper(char0);
|
|
104
|
+
return initial + lower(word.slice(1));
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function splitPrefixSuffix(input, options = {}) {
|
|
108
|
+
const splitFn = options.split ?? (options.separateNumbers ? splitSeparateNumbers : split);
|
|
109
|
+
const prefixCharacters = options.prefixCharacters ?? DEFAULT_PREFIX_SUFFIX_CHARACTERS;
|
|
110
|
+
const suffixCharacters = options.suffixCharacters ?? DEFAULT_PREFIX_SUFFIX_CHARACTERS;
|
|
111
|
+
let prefixIndex = 0;
|
|
112
|
+
let suffixIndex = input.length;
|
|
113
|
+
while (prefixIndex < input.length) {
|
|
114
|
+
const char = input.charAt(prefixIndex);
|
|
115
|
+
if (!prefixCharacters.includes(char))
|
|
116
|
+
break;
|
|
117
|
+
prefixIndex++;
|
|
118
|
+
}
|
|
119
|
+
while (suffixIndex > prefixIndex) {
|
|
120
|
+
const index = suffixIndex - 1;
|
|
121
|
+
const char = input.charAt(index);
|
|
122
|
+
if (!suffixCharacters.includes(char))
|
|
123
|
+
break;
|
|
124
|
+
suffixIndex = index;
|
|
125
|
+
}
|
|
126
|
+
return [
|
|
127
|
+
input.slice(0, prefixIndex),
|
|
128
|
+
splitFn(input.slice(prefixIndex, suffixIndex)),
|
|
129
|
+
input.slice(suffixIndex)
|
|
130
|
+
];
|
|
131
|
+
}
|
|
132
|
+
|
|
47
133
|
// libs/c4-model/src/core.ts
|
|
48
|
-
import { camelCase } from "change-case";
|
|
49
134
|
var Element = class {
|
|
50
135
|
constructor(name, defaultTags = [], definition, archetype, overrideDefinition) {
|
|
51
136
|
this.name = name;
|
|
@@ -727,7 +812,7 @@ var StructurizrDSLWriter = class {
|
|
|
727
812
|
import { glob } from "glob";
|
|
728
813
|
import { join, dirname } from "path";
|
|
729
814
|
import { fileURLToPath } from "url";
|
|
730
|
-
var
|
|
815
|
+
var _dirname = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLToPath(import.meta.url));
|
|
731
816
|
async function buildModelWithCatalog(options = {}) {
|
|
732
817
|
const { modelName = "model", modules: explicitModules } = options;
|
|
733
818
|
const model = new Model(modelName);
|
|
@@ -735,7 +820,7 @@ async function buildModelWithCatalog(options = {}) {
|
|
|
735
820
|
if (explicitModules) {
|
|
736
821
|
c4Modules = [...explicitModules];
|
|
737
822
|
} else {
|
|
738
|
-
const { globPath = "c4.dsl.ts", searchRoot =
|
|
823
|
+
const { globPath = "c4.dsl.ts", searchRoot = _dirname } = options;
|
|
739
824
|
const result = await glob(`**/${globPath}`, { cwd: searchRoot });
|
|
740
825
|
if (result.length === 0) {
|
|
741
826
|
throw new Error(`No ${globPath} files found`);
|