@agilewallaby/c4-model 2.1.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/src/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './archetype';
1
2
  export * from './model';
2
3
  export * from './person';
3
4
  export * from './softwareSystem';
@@ -5,4 +6,5 @@ export * from './container';
5
6
  export * from './component';
6
7
  export * from './views';
7
8
  export * from './structurizrDslWriter';
9
+ export * from './buildModel';
8
10
  export * from './generateDiagrams';
package/src/index.js CHANGED
@@ -1,24 +1,162 @@
1
- // libs/c4-model/src/model.ts
2
- import { glob } from "glob";
3
- import { join, dirname } from "path";
4
- import { fileURLToPath } from "url";
1
+ // libs/c4-model/src/archetype.ts
2
+ var ElementArchetype = class {
3
+ constructor(name, elementKind, definition, parent) {
4
+ this.name = name;
5
+ this.elementKind = elementKind;
6
+ this.parent = parent;
7
+ this.ownDescription = definition?.description;
8
+ this.ownTechnology = definition?.technology;
9
+ this.ownTags = definition?.tags ?? [];
10
+ this.description = this.ownDescription ?? parent?.description;
11
+ this.technology = this.ownTechnology ?? parent?.technology;
12
+ this.tags = [...parent?.tags ?? [], ...this.ownTags];
13
+ }
14
+ ownDescription;
15
+ ownTechnology;
16
+ ownTags;
17
+ description;
18
+ technology;
19
+ tags;
20
+ };
21
+ var RelationshipArchetype = class {
22
+ constructor(name, definition, parent) {
23
+ this.name = name;
24
+ this.parent = parent;
25
+ this.ownDescription = definition?.description;
26
+ this.ownTechnology = definition?.technology;
27
+ this.ownTags = definition?.tags ?? [];
28
+ this.description = this.ownDescription ?? parent?.description;
29
+ this.technology = this.ownTechnology ?? parent?.technology;
30
+ this.tags = [...parent?.tags ?? [], ...this.ownTags];
31
+ }
32
+ ownDescription;
33
+ ownTechnology;
34
+ ownTags;
35
+ description;
36
+ technology;
37
+ tags;
38
+ };
39
+ function mergeArchetypeWithOverride(archetype, override) {
40
+ return {
41
+ description: override?.description ?? archetype.description,
42
+ technology: override?.technology ?? archetype.technology,
43
+ tags: [...archetype.tags, ...override?.tags ?? []]
44
+ };
45
+ }
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
+ }
5
132
 
6
133
  // libs/c4-model/src/core.ts
7
- import { camelCase } from "change-case";
8
134
  var Element = class {
9
- constructor(name, defaultTags = [], definition) {
135
+ constructor(name, defaultTags = [], definition, archetype, overrideDefinition) {
10
136
  this.name = name;
11
137
  this.description = definition?.description;
12
138
  this.tags = (definition?.tags ?? []).concat(["Element"]).concat(defaultTags);
139
+ this.archetype = archetype;
140
+ this.overrideDefinition = overrideDefinition;
13
141
  }
14
142
  description;
15
143
  tags;
144
+ archetype;
145
+ overrideDefinition;
16
146
  _relationships = [];
17
147
  get canonicalName() {
18
148
  return camelCase(this.name);
19
149
  }
20
- uses(otherElement, definition) {
21
- const relationship = new Relationship(this, otherElement, definition);
150
+ uses(otherElement, archetypeOrDef, override) {
151
+ let definition;
152
+ let archetype;
153
+ if (archetypeOrDef instanceof RelationshipArchetype) {
154
+ archetype = archetypeOrDef;
155
+ definition = mergeArchetypeWithOverride(archetypeOrDef, override);
156
+ } else {
157
+ definition = archetypeOrDef;
158
+ }
159
+ const relationship = new Relationship(this, otherElement, definition, archetype, override);
22
160
  this._relationships.push(relationship);
23
161
  }
24
162
  get relationships() {
@@ -37,22 +175,26 @@ var Element = class {
37
175
  };
38
176
  var TechnicalElement = class extends Element {
39
177
  technology;
40
- constructor(name, defaultTags = [], definition) {
41
- super(name, defaultTags, definition);
178
+ constructor(name, defaultTags = [], definition, archetype, overrideDefinition) {
179
+ super(name, defaultTags, definition, archetype, overrideDefinition);
42
180
  this.technology = definition?.technology;
43
181
  }
44
182
  };
45
183
  var Relationship = class {
46
- constructor(source, destination, definition) {
184
+ constructor(source, destination, definition, archetype, overrideDefinition) {
47
185
  this.source = source;
48
186
  this.destination = destination;
49
187
  this.description = definition?.description;
50
188
  this.technology = definition?.technology;
51
189
  this.tags = (definition?.tags ?? []).concat(["Relationship"]);
190
+ this.archetype = archetype;
191
+ this.overrideDefinition = overrideDefinition;
52
192
  }
53
193
  description;
54
194
  tags;
55
195
  technology;
196
+ archetype;
197
+ overrideDefinition;
56
198
  };
57
199
  var Group = class {
58
200
  constructor(name) {
@@ -64,8 +206,8 @@ var Group = class {
64
206
 
65
207
  // libs/c4-model/src/component.ts
66
208
  var Component = class extends TechnicalElement {
67
- constructor(name, definition) {
68
- super(name, ["Component"], definition);
209
+ constructor(name, definition, archetype, overrideDefinition) {
210
+ super(name, ["Component"], definition, archetype, overrideDefinition);
69
211
  this.name = name;
70
212
  }
71
213
  getChildElements() {
@@ -81,8 +223,8 @@ var ContainerGroup = class extends Group {
81
223
  this.container = container;
82
224
  }
83
225
  _components = /* @__PURE__ */ new Map();
84
- defineComponent(name, definition) {
85
- const component = this.container.defineComponent(name, definition);
226
+ defineComponent(name, archetypeOrDef, override) {
227
+ const component = this.container.defineComponent(name, archetypeOrDef, override);
86
228
  this._components.set(name, component);
87
229
  return component;
88
230
  }
@@ -91,17 +233,25 @@ var ContainerGroup = class extends Group {
91
233
  }
92
234
  };
93
235
  var Container = class extends TechnicalElement {
94
- constructor(name, definition) {
95
- super(name, ["Container"], definition);
236
+ constructor(name, definition, archetype, overrideDefinition) {
237
+ super(name, ["Container"], definition, archetype, overrideDefinition);
96
238
  this.name = name;
97
239
  }
98
240
  _components = /* @__PURE__ */ new Map();
99
241
  _groups = /* @__PURE__ */ new Map();
100
- defineComponent(name, definition) {
242
+ defineComponent(name, archetypeOrDef, override) {
101
243
  if (this._components.has(name)) {
102
244
  throw Error(`A Component named '${name}' is defined elsewhere in this Container. A Component can be defined only once.`);
103
245
  }
104
- const component = new Component(name, definition);
246
+ let definition;
247
+ let archetype;
248
+ if (archetypeOrDef instanceof ElementArchetype) {
249
+ archetype = archetypeOrDef;
250
+ definition = mergeArchetypeWithOverride(archetypeOrDef, override);
251
+ } else {
252
+ definition = archetypeOrDef;
253
+ }
254
+ const component = new Component(name, definition, archetype, override);
105
255
  this._components.set(name, component);
106
256
  return component;
107
257
  }
@@ -133,8 +283,8 @@ var SoftwareSystemGroup = class extends Group {
133
283
  this.softwareSystem = softwareSystem;
134
284
  }
135
285
  _containers = /* @__PURE__ */ new Map();
136
- defineContainer(name, definition) {
137
- const container = this.softwareSystem.defineContainer(name, definition);
286
+ defineContainer(name, archetypeOrDef, override) {
287
+ const container = this.softwareSystem.defineContainer(name, archetypeOrDef, override);
138
288
  this._containers.set(name, container);
139
289
  return container;
140
290
  }
@@ -143,17 +293,25 @@ var SoftwareSystemGroup = class extends Group {
143
293
  }
144
294
  };
145
295
  var SoftwareSystem = class extends Element {
146
- constructor(name, definition) {
147
- super(name, ["Software System"], definition);
296
+ constructor(name, definition, archetype, overrideDefinition) {
297
+ super(name, ["Software System"], definition, archetype, overrideDefinition);
148
298
  this.name = name;
149
299
  }
150
300
  _containers = /* @__PURE__ */ new Map();
151
301
  _groups = /* @__PURE__ */ new Map();
152
- defineContainer(name, definition) {
302
+ defineContainer(name, archetypeOrDef, override) {
153
303
  if (this._containers.has(name)) {
154
304
  throw Error(`A Container named '${name}' is defined elsewhere in this SoftwareSystem. A Container can be defined only once.`);
155
305
  }
156
- const container = new Container(name, definition);
306
+ let definition;
307
+ let archetype;
308
+ if (archetypeOrDef instanceof ElementArchetype) {
309
+ archetype = archetypeOrDef;
310
+ definition = mergeArchetypeWithOverride(archetypeOrDef, override);
311
+ } else {
312
+ definition = archetypeOrDef;
313
+ }
314
+ const container = new Container(name, definition, archetype, override);
157
315
  this._containers.set(name, container);
158
316
  return container;
159
317
  }
@@ -179,8 +337,8 @@ var SoftwareSystem = class extends Element {
179
337
 
180
338
  // libs/c4-model/src/person.ts
181
339
  var Person = class extends Element {
182
- constructor(name, definition) {
183
- super(name, ["Person"], definition);
340
+ constructor(name, definition, archetype, overrideDefinition) {
341
+ super(name, ["Person"], definition, archetype, overrideDefinition);
184
342
  this.name = name;
185
343
  }
186
344
  getChildElements() {
@@ -189,7 +347,6 @@ var Person = class extends Element {
189
347
  };
190
348
 
191
349
  // libs/c4-model/src/model.ts
192
- var __dirname = dirname(fileURLToPath(import.meta.url));
193
350
  var ModelGroup = class extends Group {
194
351
  constructor(name, model) {
195
352
  super(name);
@@ -198,13 +355,13 @@ var ModelGroup = class extends Group {
198
355
  }
199
356
  softwareSystems = /* @__PURE__ */ new Map();
200
357
  people = /* @__PURE__ */ new Map();
201
- defineSoftwareSystem(name, definition) {
202
- const softwareSystem = this.model.defineSoftwareSystem(name, definition);
358
+ defineSoftwareSystem(name, archetypeOrDef, override) {
359
+ const softwareSystem = this.model.defineSoftwareSystem(name, archetypeOrDef, override);
203
360
  this.softwareSystems.set(name, softwareSystem);
204
361
  return softwareSystem;
205
362
  }
206
- definePerson(name, definition) {
207
- const person = this.model.definePerson(name, definition);
363
+ definePerson(name, archetypeOrDef, override) {
364
+ const person = this.model.definePerson(name, archetypeOrDef, override);
208
365
  this.people.set(name, person);
209
366
  return person;
210
367
  }
@@ -222,11 +379,19 @@ var Model = class {
222
379
  softwareSystems = /* @__PURE__ */ new Map();
223
380
  people = /* @__PURE__ */ new Map();
224
381
  groups = /* @__PURE__ */ new Map();
225
- defineSoftwareSystem(name, definition) {
382
+ defineSoftwareSystem(name, archetypeOrDef, override) {
226
383
  if (this.softwareSystems.has(name)) {
227
384
  throw Error(`A SoftwareSystem named '${name}' is defined elsewhere in this Model. A SoftwareSystem can be defined only once.`);
228
385
  }
229
- const system = new SoftwareSystem(name, definition);
386
+ let definition;
387
+ let archetype;
388
+ if (archetypeOrDef instanceof ElementArchetype) {
389
+ archetype = archetypeOrDef;
390
+ definition = mergeArchetypeWithOverride(archetypeOrDef, override);
391
+ } else {
392
+ definition = archetypeOrDef;
393
+ }
394
+ const system = new SoftwareSystem(name, definition, archetype, override);
230
395
  this.softwareSystems.set(name, system);
231
396
  return system;
232
397
  }
@@ -239,11 +404,19 @@ var Model = class {
239
404
  }
240
405
  return group;
241
406
  }
242
- definePerson(name, definition) {
407
+ definePerson(name, archetypeOrDef, override) {
243
408
  if (this.people.has(name)) {
244
409
  throw Error(`A Person named '${name}' is defined elsewhere in this Model. A Person can be defined only once.`);
245
410
  }
246
- const person = new Person(name, definition);
411
+ let definition;
412
+ let archetype;
413
+ if (archetypeOrDef instanceof ElementArchetype) {
414
+ archetype = archetypeOrDef;
415
+ definition = mergeArchetypeWithOverride(archetypeOrDef, override);
416
+ } else {
417
+ definition = archetypeOrDef;
418
+ }
419
+ const person = new Person(name, definition, archetype, override);
247
420
  this.people.set(name, person);
248
421
  return person;
249
422
  }
@@ -267,37 +440,6 @@ var Model = class {
267
440
  return Array.from(this.groups.values());
268
441
  }
269
442
  };
270
- async function buildModelWithCatalog(options = {}) {
271
- const { modelName = "model", globPath = "c4.dsl.ts", searchRoot = __dirname } = options;
272
- const model = new Model(modelName);
273
- const result = await glob(`**/${globPath}`, { cwd: searchRoot });
274
- if (result.length === 0) {
275
- throw new Error(`No ${globPath} files found`);
276
- }
277
- const modules = await Promise.all(result.map((file) => import(join(searchRoot, file))));
278
- const registrations = [];
279
- const rootCatalog = {};
280
- for (const module of modules) {
281
- if (!module.c4Module) {
282
- continue;
283
- }
284
- const instance = module.c4Module;
285
- const local = instance.registerDefinitions(model);
286
- rootCatalog[instance.key] = local;
287
- registrations.push({ instance, key: instance.key, local });
288
- }
289
- if (registrations.length === 0) {
290
- throw new Error(`No c4Module exports found in any ${globPath} files`);
291
- }
292
- for (const { instance, key, local } of registrations) {
293
- const dependencies = Object.fromEntries(Object.entries(rootCatalog).filter(([k]) => k !== key));
294
- instance.buildRelationships(local, dependencies);
295
- }
296
- return { model, catalog: rootCatalog };
297
- }
298
- async function buildModel(options = {}) {
299
- return (await buildModelWithCatalog(options)).model;
300
- }
301
443
 
302
444
  // libs/c4-model/src/views.ts
303
445
  var View = class {
@@ -379,13 +521,115 @@ var StructurizrDSLWriter = class {
379
521
  this.model = model;
380
522
  this.views = views;
381
523
  }
524
+ collectArchetypes() {
525
+ const elementSet = /* @__PURE__ */ new Set();
526
+ const relationshipSet = /* @__PURE__ */ new Set();
527
+ const collectFromElement = (element) => {
528
+ if (element.archetype) {
529
+ let arch = element.archetype;
530
+ while (arch) {
531
+ elementSet.add(arch);
532
+ arch = arch.parent;
533
+ }
534
+ }
535
+ element.relationships.forEach((rel) => {
536
+ if (rel.archetype) {
537
+ let arch = rel.archetype;
538
+ while (arch) {
539
+ relationshipSet.add(arch);
540
+ arch = arch.parent;
541
+ }
542
+ }
543
+ });
544
+ element.getChildElements().forEach(collectFromElement);
545
+ };
546
+ const allElements = [...this.model.getPeople(), ...this.model.getSoftwareSystems()];
547
+ allElements.forEach(collectFromElement);
548
+ const sortArchetypes = (set) => {
549
+ const sorted = [];
550
+ const visited = /* @__PURE__ */ new Set();
551
+ const visit = (arch) => {
552
+ if (visited.has(arch)) return;
553
+ if (arch.parent && set.has(arch.parent)) visit(arch.parent);
554
+ visited.add(arch);
555
+ sorted.push(arch);
556
+ };
557
+ set.forEach(visit);
558
+ return sorted;
559
+ };
560
+ return {
561
+ elementArchetypes: sortArchetypes(elementSet),
562
+ relationshipArchetypes: sortArchetypes(relationshipSet)
563
+ };
564
+ }
565
+ writeArchetypes(level) {
566
+ const { elementArchetypes, relationshipArchetypes } = this.collectArchetypes();
567
+ if (elementArchetypes.length === 0 && relationshipArchetypes.length === 0) return "";
568
+ let dsl = this.writeLine(`archetypes {`, level);
569
+ for (const arch of elementArchetypes) {
570
+ const baseType = arch.parent ? arch.parent.name : arch.elementKind;
571
+ let inner = "";
572
+ if (arch.ownDescription) {
573
+ inner += this.writeLine(`description "${arch.ownDescription}"`, level + 2);
574
+ }
575
+ if (arch.ownTechnology) {
576
+ inner += this.writeLine(`technology "${arch.ownTechnology}"`, level + 2);
577
+ }
578
+ if (arch.ownTags.length > 0) {
579
+ inner += this.writeLine(`tags ${arch.ownTags.map((t) => `"${t}"`).join(" ")}`, level + 2);
580
+ }
581
+ if (inner) {
582
+ dsl += this.writeLine(`${arch.name} = ${baseType} {`, level + 1);
583
+ dsl += inner;
584
+ dsl += this.writeLine(`}`, level + 1);
585
+ } else {
586
+ dsl += this.writeLine(`${arch.name} = ${baseType} {}`, level + 1);
587
+ }
588
+ }
589
+ for (const arch of relationshipArchetypes) {
590
+ const arrow = arch.parent ? `--${arch.parent.name}->` : `->`;
591
+ let inner = "";
592
+ if (arch.ownDescription) {
593
+ inner += this.writeLine(`description "${arch.ownDescription}"`, level + 2);
594
+ }
595
+ if (arch.ownTechnology) {
596
+ inner += this.writeLine(`technology "${arch.ownTechnology}"`, level + 2);
597
+ }
598
+ if (arch.ownTags.length > 0) {
599
+ inner += this.writeLine(`tags ${arch.ownTags.map((t) => `"${t}"`).join(" ")}`, level + 2);
600
+ }
601
+ if (inner) {
602
+ dsl += this.writeLine(`${arch.name} = ${arrow} {`, level + 1);
603
+ dsl += inner;
604
+ dsl += this.writeLine(`}`, level + 1);
605
+ } else {
606
+ dsl += this.writeLine(`${arch.name} = ${arrow} {}`, level + 1);
607
+ }
608
+ }
609
+ dsl += this.writeLine(`}`, level);
610
+ return dsl;
611
+ }
382
612
  writeElement(elementType, element, level, closeElement = true) {
383
613
  let elementDsl = "";
384
- elementDsl += this.writeLine(`${element.canonicalName} = ${elementType} "${element.name}" {`, level);
385
- if (element.description) {
386
- elementDsl += this.writeLine(`description "${element.description}"`, level + 1);
614
+ const type = element.archetype ? element.archetype.name : elementType;
615
+ elementDsl += this.writeLine(`${element.canonicalName} = ${type} "${element.name}" {`, level);
616
+ if (element.archetype) {
617
+ const ovr = element.overrideDefinition;
618
+ if (ovr?.description) {
619
+ elementDsl += this.writeLine(`description "${ovr.description}"`, level + 1);
620
+ }
621
+ if (ovr?.tags && ovr.tags.length > 0) {
622
+ elementDsl += this.writeLine(`tags ${ovr.tags.map((tag) => `"${tag}"`).join(" ")}`, level + 1);
623
+ }
624
+ if (ovr && "technology" in ovr && ovr.technology) {
625
+ elementDsl += this.writeLine(`technology "${ovr.technology}"`, level + 1);
626
+ }
627
+ } else {
628
+ if (element.description) {
629
+ elementDsl += this.writeLine(`description "${element.description}"`, level + 1);
630
+ }
631
+ elementDsl += this.writeLine(`tags ${element.tags.map((tag) => `"${tag}"`).join(" ")}`, level + 1);
387
632
  }
388
- elementDsl += this.writeLine(`tags ${element.tags.map((tag) => `"${tag}"`).join(" ")}`, level + 1);
389
633
  if (closeElement) {
390
634
  elementDsl += this.writeLine(`}`, level);
391
635
  }
@@ -394,7 +638,9 @@ var StructurizrDSLWriter = class {
394
638
  writeComponent(component, level) {
395
639
  let componentDsl = "";
396
640
  componentDsl += this.writeElement("component", component, level, false);
397
- componentDsl += this.writeLine(`technology "${component.technology}"`, level + 1);
641
+ if (!component.archetype && component.technology) {
642
+ componentDsl += this.writeLine(`technology "${component.technology}"`, level + 1);
643
+ }
398
644
  componentDsl += this.writeLine(`}`, level);
399
645
  return componentDsl;
400
646
  }
@@ -410,7 +656,9 @@ var StructurizrDSLWriter = class {
410
656
  writeContainer(container, level) {
411
657
  let containerDsl = "";
412
658
  containerDsl += this.writeElement("container", container, level, false);
413
- containerDsl += this.writeLine(`technology "${container.technology}"`, level + 1);
659
+ if (!container.archetype && container.technology) {
660
+ containerDsl += this.writeLine(`technology "${container.technology}"`, level + 1);
661
+ }
414
662
  container.getComponentsNotInGroups().forEach((component) => {
415
663
  containerDsl += this.writeComponent(component, level + 1);
416
664
  });
@@ -441,17 +689,39 @@ var StructurizrDSLWriter = class {
441
689
  softwareSystemDsl += this.writeLine(`}`, level);
442
690
  return softwareSystemDsl;
443
691
  }
692
+ writeRelationship(relationship, level) {
693
+ let dsl = "";
694
+ if (relationship.archetype) {
695
+ const arrow = `--${relationship.archetype.name}->`;
696
+ const ovr = relationship.overrideDefinition;
697
+ const desc = ovr?.description ?? relationship.description ?? "uses";
698
+ dsl += this.writeLine(
699
+ `${relationship.source.canonicalName} ${arrow} ${relationship.destination.canonicalName} "${desc}" {`,
700
+ level
701
+ );
702
+ if (ovr?.technology) {
703
+ dsl += this.writeLine(`technology "${ovr.technology}"`, level + 1);
704
+ }
705
+ if (ovr?.tags && ovr.tags.length > 0) {
706
+ dsl += this.writeLine(`tags ${ovr.tags.map((tag) => `"${tag}"`).join(" ")}`, level + 1);
707
+ }
708
+ dsl += this.writeLine(`}`, level);
709
+ } else {
710
+ const tech = relationship.technology ? ` "${relationship.technology}"` : "";
711
+ dsl += this.writeLine(
712
+ `${relationship.source.canonicalName} -> ${relationship.destination.canonicalName} "${relationship.description ?? "uses"}"${tech} {`,
713
+ level
714
+ );
715
+ dsl += this.writeLine(`tags ${relationship.tags.map((tag) => `"${tag}"`).join(" ")}`, level + 1);
716
+ dsl += this.writeLine(`}`, level);
717
+ }
718
+ return dsl;
719
+ }
444
720
  writeRelationships(elements, level) {
445
721
  let relationshipsDsl = "";
446
722
  elements.forEach((element) => {
447
723
  element.getRelationshipsInHierarchy().forEach((relationship) => {
448
- const tech = relationship.technology ? ` "${relationship.technology}"` : "";
449
- relationshipsDsl += this.writeLine(
450
- `${relationship.source.canonicalName} -> ${relationship.destination.canonicalName} "${relationship.description ?? "uses"}"${tech} {`,
451
- level
452
- );
453
- relationshipsDsl += this.writeLine(`tags ${relationship.tags.map((tag) => `"${tag}"`).join(" ")}`, level + 1);
454
- relationshipsDsl += this.writeLine(`}`, level);
724
+ relationshipsDsl += this.writeRelationship(relationship, level);
455
725
  });
456
726
  });
457
727
  return relationshipsDsl;
@@ -471,6 +741,7 @@ var StructurizrDSLWriter = class {
471
741
  writeModel(model, level) {
472
742
  let modelDsl = "";
473
743
  modelDsl += this.writeLine(`model {`, level);
744
+ modelDsl += this.writeArchetypes(level + 1);
474
745
  modelDsl += this.writeLine("// Elements", level + 1);
475
746
  model.getPeopleNotInGroups().forEach((person) => {
476
747
  modelDsl += this.writeElement("person", person, level + 1);
@@ -487,10 +758,7 @@ var StructurizrDSLWriter = class {
487
758
  return modelDsl;
488
759
  }
489
760
  writeView(view, viewType, level) {
490
- let viewDsl = this.writeLine(
491
- `${viewType}${view.subject ? ' "' + view.subject.canonicalName + '"' : ""} "${view.key}" {`,
492
- level
493
- );
761
+ let viewDsl = this.writeLine(`${viewType}${view.subject ? ' "' + view.subject.canonicalName + '"' : ""} "${view.key}" {`, level);
494
762
  viewDsl += this.writeLine(`description "${view.description}"`, level + 1);
495
763
  if (view.title) {
496
764
  viewDsl += this.writeLine(`title "${view.title}"`, level + 1);
@@ -540,6 +808,46 @@ var StructurizrDSLWriter = class {
540
808
  }
541
809
  };
542
810
 
811
+ // libs/c4-model/src/buildModel.ts
812
+ import { glob } from "glob";
813
+ import { join, dirname } from "path";
814
+ import { fileURLToPath } from "url";
815
+ var _dirname = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLToPath(import.meta.url));
816
+ async function buildModelWithCatalog(options = {}) {
817
+ const { modelName = "model", modules: explicitModules } = options;
818
+ const model = new Model(modelName);
819
+ let c4Modules;
820
+ if (explicitModules) {
821
+ c4Modules = [...explicitModules];
822
+ } else {
823
+ const { globPath = "c4.dsl.ts", searchRoot = _dirname } = options;
824
+ const result = await glob(`**/${globPath}`, { cwd: searchRoot });
825
+ if (result.length === 0) {
826
+ throw new Error(`No ${globPath} files found`);
827
+ }
828
+ const imported = await Promise.all(result.map((file) => import(join(searchRoot, file))));
829
+ c4Modules = imported.filter((m) => m.c4Module).map((m) => m.c4Module);
830
+ }
831
+ if (c4Modules.length === 0) {
832
+ throw new Error("No c4Module instances found");
833
+ }
834
+ const registrations = [];
835
+ const rootCatalog = {};
836
+ for (const instance of c4Modules) {
837
+ const local = instance.registerDefinitions(model);
838
+ rootCatalog[instance.key] = local;
839
+ registrations.push({ instance, key: instance.key, local });
840
+ }
841
+ for (const { instance, key, local } of registrations) {
842
+ const dependencies = Object.fromEntries(Object.entries(rootCatalog).filter(([k]) => k !== key));
843
+ instance.buildRelationships(local, dependencies);
844
+ }
845
+ return { model, catalog: rootCatalog };
846
+ }
847
+ async function buildModel(options = {}) {
848
+ return (await buildModelWithCatalog(options)).model;
849
+ }
850
+
543
851
  // libs/c4-model/src/generateDiagrams.ts
544
852
  import * as fs from "fs";
545
853
  import * as os from "os";
@@ -573,9 +881,11 @@ export {
573
881
  Component,
574
882
  Container,
575
883
  ContainerGroup,
884
+ ElementArchetype,
576
885
  Model,
577
886
  ModelGroup,
578
887
  Person,
888
+ RelationshipArchetype,
579
889
  SoftwareSystem,
580
890
  SoftwareSystemGroup,
581
891
  StructurizrDSLWriter,
@@ -583,5 +893,6 @@ export {
583
893
  Views,
584
894
  buildModel,
585
895
  buildModelWithCatalog,
586
- generateDiagrams
896
+ generateDiagrams,
897
+ mergeArchetypeWithOverride
587
898
  };