@agilewallaby/c4-model 2.7.0 → 3.0.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.js CHANGED
@@ -1,49 +1,3 @@
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
1
  // node_modules/.pnpm/change-case@5.4.4/node_modules/change-case/dist/index.js
48
2
  var SPLIT_LOWER_UPPER_RE = /([\p{Ll}\d])(\p{Lu})/gu;
49
3
  var SPLIT_UPPER_UPPER_RE = /(\p{Lu})([\p{Lu}][\p{Ll}])/gu;
@@ -130,6 +84,64 @@ function splitPrefixSuffix(input, options = {}) {
130
84
  ];
131
85
  }
132
86
 
87
+ // libs/c4-model/src/archetype.ts
88
+ var ELEMENT_KINDS = {
89
+ person: "person",
90
+ softwareSystem: "softwareSystem",
91
+ container: "container",
92
+ component: "component"
93
+ };
94
+ var ElementArchetype = class {
95
+ constructor(name, elementKind, definition, parent) {
96
+ this.name = name;
97
+ this.elementKind = elementKind;
98
+ this.parent = parent;
99
+ this.ownDescription = definition?.description;
100
+ this.ownTechnology = definition?.technology;
101
+ this.ownTags = definition?.tags ?? [];
102
+ this.description = this.ownDescription ?? parent?.description;
103
+ this.technology = this.ownTechnology ?? parent?.technology;
104
+ this.tags = [...parent?.tags ?? [], ...this.ownTags];
105
+ }
106
+ ownDescription;
107
+ ownTechnology;
108
+ ownTags;
109
+ description;
110
+ technology;
111
+ tags;
112
+ get canonicalName() {
113
+ return camelCase(this.name);
114
+ }
115
+ };
116
+ var RelationshipArchetype = class {
117
+ constructor(name, definition, parent) {
118
+ this.name = name;
119
+ this.parent = parent;
120
+ this.ownDescription = definition?.description;
121
+ this.ownTechnology = definition?.technology;
122
+ this.ownTags = definition?.tags ?? [];
123
+ this.description = this.ownDescription ?? parent?.description;
124
+ this.technology = this.ownTechnology ?? parent?.technology;
125
+ this.tags = [...parent?.tags ?? [], ...this.ownTags];
126
+ }
127
+ ownDescription;
128
+ ownTechnology;
129
+ ownTags;
130
+ description;
131
+ technology;
132
+ tags;
133
+ get canonicalName() {
134
+ return camelCase(this.name);
135
+ }
136
+ };
137
+ function mergeArchetypeWithOverride(archetype, override) {
138
+ return {
139
+ description: override?.description ?? archetype.description,
140
+ technology: override?.technology ?? archetype.technology,
141
+ tags: [...archetype.tags, ...override?.tags ?? []]
142
+ };
143
+ }
144
+
133
145
  // libs/c4-model/src/core.ts
134
146
  var Element = class {
135
147
  constructor(name, defaultTags = [], definition, archetype, overrideDefinition) {
@@ -169,9 +181,9 @@ var Element = class {
169
181
  getRelationshipsInHierarchy() {
170
182
  return this._relationships.concat(this.getChildElements().flatMap((element) => element.getRelationshipsInHierarchy()));
171
183
  }
172
- getChildElementNames(path2) {
184
+ getChildElementNames(path4) {
173
185
  const result = Array.from(this.getChildElements()).flatMap((reference) => {
174
- const currentPath = `${path2 ? path2 : "" + this.name}.${reference.name}`;
186
+ const currentPath = `${path4 ? path4 : "" + this.name}.${reference.name}`;
175
187
  return [currentPath, ...reference.getChildElementNames(currentPath)];
176
188
  });
177
189
  return result;
@@ -227,21 +239,46 @@ var Component = class extends TechnicalElement {
227
239
  };
228
240
 
229
241
  // libs/c4-model/src/container.ts
230
- var ContainerGroup = class extends Group {
231
- constructor(name, container) {
242
+ var ContainerGroup = class _ContainerGroup extends Group {
243
+ constructor(name, container, pathSegments = []) {
232
244
  super(name);
233
245
  this.name = name;
234
246
  this.container = container;
247
+ this.pathSegments = pathSegments;
235
248
  }
236
249
  _components = /* @__PURE__ */ new Map();
250
+ _groups = /* @__PURE__ */ new Map();
251
+ get canonicalName() {
252
+ return camelCase([...this.pathSegments, this.name].join(" "));
253
+ }
254
+ get dslName() {
255
+ return [...this.pathSegments, this.name].join("/");
256
+ }
237
257
  component(name, archetypeOrDef, override) {
238
258
  const component = this.container.component(name, archetypeOrDef, override);
239
259
  this._components.set(name, component);
240
260
  return component;
241
261
  }
262
+ group(groupName) {
263
+ let group = this._groups.get(groupName);
264
+ if (!group) {
265
+ group = new _ContainerGroup(groupName, this.container, [...this.pathSegments, this.name]);
266
+ this._groups.set(groupName, group);
267
+ }
268
+ return group;
269
+ }
270
+ getGroups() {
271
+ return Array.from(this._groups.values());
272
+ }
242
273
  getComponents() {
243
274
  return Array.from(this._components.values());
244
275
  }
276
+ getAllComponents() {
277
+ return [
278
+ ...this._components.values(),
279
+ ...Array.from(this._groups.values()).flatMap((g) => g.getAllComponents())
280
+ ];
281
+ }
245
282
  };
246
283
  var Container = class extends TechnicalElement {
247
284
  constructor(name, definition, archetype, overrideDefinition) {
@@ -278,7 +315,7 @@ var Container = class extends TechnicalElement {
278
315
  return Array.from(this._groups.values());
279
316
  }
280
317
  getComponentsNotInGroups() {
281
- const componentsInGroups = this.getGroups().flatMap((group) => group.getComponents());
318
+ const componentsInGroups = this.getGroups().flatMap((group) => group.getAllComponents());
282
319
  return Array.from(this._components.values()).filter((component) => !componentsInGroups.includes(component));
283
320
  }
284
321
  getChildElements() {
@@ -287,21 +324,46 @@ var Container = class extends TechnicalElement {
287
324
  };
288
325
 
289
326
  // libs/c4-model/src/softwareSystem.ts
290
- var SoftwareSystemGroup = class extends Group {
291
- constructor(name, softwareSystem) {
327
+ var SoftwareSystemGroup = class _SoftwareSystemGroup extends Group {
328
+ constructor(name, softwareSystem, pathSegments = []) {
292
329
  super(name);
293
330
  this.name = name;
294
331
  this.softwareSystem = softwareSystem;
332
+ this.pathSegments = pathSegments;
295
333
  }
296
334
  _containers = /* @__PURE__ */ new Map();
335
+ _groups = /* @__PURE__ */ new Map();
336
+ get canonicalName() {
337
+ return camelCase([...this.pathSegments, this.name].join(" "));
338
+ }
339
+ get dslName() {
340
+ return [...this.pathSegments, this.name].join("/");
341
+ }
297
342
  container(name, archetypeOrDef, override) {
298
343
  const container = this.softwareSystem.container(name, archetypeOrDef, override);
299
344
  this._containers.set(name, container);
300
345
  return container;
301
346
  }
347
+ group(groupName) {
348
+ let group = this._groups.get(groupName);
349
+ if (!group) {
350
+ group = new _SoftwareSystemGroup(groupName, this.softwareSystem, [...this.pathSegments, this.name]);
351
+ this._groups.set(groupName, group);
352
+ }
353
+ return group;
354
+ }
355
+ getGroups() {
356
+ return Array.from(this._groups.values());
357
+ }
302
358
  getContainers() {
303
359
  return Array.from(this._containers.values());
304
360
  }
361
+ getAllContainers() {
362
+ return [
363
+ ...this._containers.values(),
364
+ ...Array.from(this._groups.values()).flatMap((g) => g.getAllContainers())
365
+ ];
366
+ }
305
367
  };
306
368
  var SoftwareSystem = class extends Element {
307
369
  constructor(name, definition, archetype, overrideDefinition) {
@@ -341,7 +403,7 @@ var SoftwareSystem = class extends Element {
341
403
  return Array.from(this._containers.values());
342
404
  }
343
405
  getContainersNotInGroups() {
344
- const containersInGroups = Array.from(this._groups.values()).flatMap((group) => group.getContainers());
406
+ const containersInGroups = Array.from(this._groups.values()).flatMap((group) => group.getAllContainers());
345
407
  return Array.from(this._containers.values()).filter((container) => !containersInGroups.includes(container));
346
408
  }
347
409
  };
@@ -358,14 +420,22 @@ var Person = class extends Element {
358
420
  };
359
421
 
360
422
  // libs/c4-model/src/model.ts
361
- var ModelGroup = class extends Group {
362
- constructor(name, model) {
423
+ var ModelGroup = class _ModelGroup extends Group {
424
+ constructor(name, model, pathSegments = []) {
363
425
  super(name);
364
426
  this.name = name;
365
427
  this.model = model;
428
+ this.pathSegments = pathSegments;
366
429
  }
367
430
  softwareSystems = /* @__PURE__ */ new Map();
368
431
  people = /* @__PURE__ */ new Map();
432
+ _groups = /* @__PURE__ */ new Map();
433
+ get canonicalName() {
434
+ return camelCase([...this.pathSegments, this.name].join(" "));
435
+ }
436
+ get dslName() {
437
+ return [...this.pathSegments, this.name].join("/");
438
+ }
369
439
  softwareSystem(name, archetypeOrDef, override) {
370
440
  const softwareSystem = this.model.softwareSystem(name, archetypeOrDef, override);
371
441
  this.softwareSystems.set(name, softwareSystem);
@@ -376,12 +446,35 @@ var ModelGroup = class extends Group {
376
446
  this.people.set(name, person);
377
447
  return person;
378
448
  }
449
+ group(groupName) {
450
+ let group = this._groups.get(groupName);
451
+ if (!group) {
452
+ group = new _ModelGroup(groupName, this.model, [...this.pathSegments, this.name]);
453
+ this._groups.set(groupName, group);
454
+ }
455
+ return group;
456
+ }
457
+ getGroups() {
458
+ return Array.from(this._groups.values());
459
+ }
379
460
  getSoftwareSystems() {
380
461
  return Array.from(this.softwareSystems.values());
381
462
  }
382
463
  getPeople() {
383
464
  return Array.from(this.people.values());
384
465
  }
466
+ getAllSoftwareSystems() {
467
+ return [
468
+ ...this.softwareSystems.values(),
469
+ ...Array.from(this._groups.values()).flatMap((g) => g.getAllSoftwareSystems())
470
+ ];
471
+ }
472
+ getAllPeople() {
473
+ return [
474
+ ...this.people.values(),
475
+ ...Array.from(this._groups.values()).flatMap((g) => g.getAllPeople())
476
+ ];
477
+ }
385
478
  };
386
479
  var Model = class {
387
480
  constructor(name) {
@@ -406,7 +499,6 @@ var Model = class {
406
499
  this.softwareSystems.set(name, system);
407
500
  return system;
408
501
  }
409
- // TODO:Should be a Group<SoftwareSystem | Person> if that is added back in
410
502
  group(groupName) {
411
503
  let group = this.groups.get(groupName);
412
504
  if (!group) {
@@ -441,11 +533,11 @@ var Model = class {
441
533
  return Array.from(this.softwareSystems.values());
442
534
  }
443
535
  getPeopleNotInGroups() {
444
- const peopleInGroups = Array.from(this.groups.values()).flatMap((group) => group.getPeople());
536
+ const peopleInGroups = Array.from(this.groups.values()).flatMap((group) => group.getAllPeople());
445
537
  return Array.from(this.people.values()).filter((person) => !peopleInGroups.includes(person));
446
538
  }
447
539
  getSoftwareSystemsNotInGroups() {
448
- const systemsInGroups = Array.from(this.groups.values()).flatMap((group) => group.getSoftwareSystems());
540
+ const systemsInGroups = Array.from(this.groups.values()).flatMap((group) => group.getAllSoftwareSystems());
449
541
  return Array.from(this.softwareSystems.values()).filter((system) => !systemsInGroups.includes(system));
450
542
  }
451
543
  getGroups() {
@@ -465,6 +557,9 @@ var View = class {
465
557
  description;
466
558
  title;
467
559
  _scopes = [];
560
+ _autoLayout;
561
+ _isDefault = false;
562
+ _properties = /* @__PURE__ */ new Map();
468
563
  includeAll() {
469
564
  this._scopes.push("include *");
470
565
  }
@@ -483,15 +578,37 @@ var View = class {
483
578
  excludeExpression(expression) {
484
579
  this._scopes.push(`exclude ${expression}`);
485
580
  }
581
+ autoLayout(direction, rankSeparation, nodeSeparation) {
582
+ this._autoLayout = { direction, rankSeparation, nodeSeparation };
583
+ }
584
+ setDefault() {
585
+ this._isDefault = true;
586
+ }
587
+ addProperty(name, value) {
588
+ this._properties.set(name, value);
589
+ }
486
590
  get scopes() {
487
591
  return this._scopes;
488
592
  }
593
+ get autoLayoutConfig() {
594
+ return this._autoLayout;
595
+ }
596
+ get isDefault() {
597
+ return this._isDefault;
598
+ }
599
+ get properties() {
600
+ return this._properties;
601
+ }
489
602
  };
490
603
  var Views = class {
491
604
  _systemLandscapeViews = /* @__PURE__ */ new Map();
492
605
  _systemContextViews = /* @__PURE__ */ new Map();
493
606
  _containerViews = /* @__PURE__ */ new Map();
494
607
  _componentViews = /* @__PURE__ */ new Map();
608
+ _elementStyles = [];
609
+ _relationshipStyles = [];
610
+ _themes = [];
611
+ _properties = /* @__PURE__ */ new Map();
495
612
  addSystemLandscapeView(key, definition) {
496
613
  const view = new View(key, { subject: void 0, description: definition.description, title: definition.title });
497
614
  this._systemLandscapeViews.set(key, view);
@@ -512,6 +629,18 @@ var Views = class {
512
629
  this._componentViews.set(key, view);
513
630
  return view;
514
631
  }
632
+ addElementStyle(tag, definition) {
633
+ this._elementStyles.push({ tag, definition });
634
+ }
635
+ addRelationshipStyle(tag, definition) {
636
+ this._relationshipStyles.push({ tag, definition });
637
+ }
638
+ addTheme(url) {
639
+ this._themes.push(url);
640
+ }
641
+ addProperty(name, value) {
642
+ this._properties.set(name, value);
643
+ }
515
644
  get systemLandscapeViews() {
516
645
  return Array.from(this._systemLandscapeViews.values());
517
646
  }
@@ -524,6 +653,18 @@ var Views = class {
524
653
  get componentViews() {
525
654
  return Array.from(this._componentViews.values());
526
655
  }
656
+ get elementStyles() {
657
+ return this._elementStyles;
658
+ }
659
+ get relationshipStyles() {
660
+ return this._relationshipStyles;
661
+ }
662
+ get themes() {
663
+ return this._themes;
664
+ }
665
+ get properties() {
666
+ return this._properties;
667
+ }
527
668
  };
528
669
 
529
670
  // libs/c4-model/src/structurizrDslWriter.ts
@@ -579,7 +720,7 @@ var StructurizrDSLWriter = class {
579
720
  if (elementArchetypes.length === 0 && relationshipArchetypes.length === 0) return "";
580
721
  let dsl = this.writeLine(`archetypes {`, level);
581
722
  for (const arch of elementArchetypes) {
582
- const baseType = arch.parent ? arch.parent.name : arch.elementKind;
723
+ const baseType = arch.parent ? arch.parent.canonicalName : arch.elementKind;
583
724
  let inner = "";
584
725
  if (arch.ownDescription) {
585
726
  inner += this.writeLine(`description "${arch.ownDescription}"`, level + 2);
@@ -591,15 +732,15 @@ var StructurizrDSLWriter = class {
591
732
  inner += this.writeLine(`tags ${arch.ownTags.map((t) => `"${t}"`).join(" ")}`, level + 2);
592
733
  }
593
734
  if (inner) {
594
- dsl += this.writeLine(`${arch.name} = ${baseType} {`, level + 1);
735
+ dsl += this.writeLine(`${arch.canonicalName} = ${baseType} {`, level + 1);
595
736
  dsl += inner;
596
737
  dsl += this.writeLine(`}`, level + 1);
597
738
  } else {
598
- dsl += this.writeLine(`${arch.name} = ${baseType} {}`, level + 1);
739
+ dsl += this.writeLine(`${arch.canonicalName} = ${baseType} {}`, level + 1);
599
740
  }
600
741
  }
601
742
  for (const arch of relationshipArchetypes) {
602
- const arrow = arch.parent ? `--${arch.parent.name}->` : `->`;
743
+ const arrow = arch.parent ? `--${arch.parent.canonicalName}->` : `->`;
603
744
  let inner = "";
604
745
  if (arch.ownDescription) {
605
746
  inner += this.writeLine(`description "${arch.ownDescription}"`, level + 2);
@@ -611,11 +752,11 @@ var StructurizrDSLWriter = class {
611
752
  inner += this.writeLine(`tags ${arch.ownTags.map((t) => `"${t}"`).join(" ")}`, level + 2);
612
753
  }
613
754
  if (inner) {
614
- dsl += this.writeLine(`${arch.name} = ${arrow} {`, level + 1);
755
+ dsl += this.writeLine(`${arch.canonicalName} = ${arrow} {`, level + 1);
615
756
  dsl += inner;
616
757
  dsl += this.writeLine(`}`, level + 1);
617
758
  } else {
618
- dsl += this.writeLine(`${arch.name} = ${arrow} {}`, level + 1);
759
+ dsl += this.writeLine(`${arch.canonicalName} = ${arrow} {}`, level + 1);
619
760
  }
620
761
  }
621
762
  dsl += this.writeLine(`}`, level);
@@ -623,7 +764,7 @@ var StructurizrDSLWriter = class {
623
764
  }
624
765
  writeElement(elementType, element, level, closeElement = true) {
625
766
  let elementDsl = "";
626
- const type = element.archetype ? element.archetype.name : elementType;
767
+ const type = element.archetype ? element.archetype.canonicalName : elementType;
627
768
  elementDsl += this.writeLine(`${element.canonicalName} = ${type} "${element.name}" {`, level);
628
769
  if (element.archetype) {
629
770
  const ovr = element.overrideDefinition;
@@ -658,11 +799,17 @@ var StructurizrDSLWriter = class {
658
799
  }
659
800
  writeContainerGroup(group, level) {
660
801
  let containerGroupDsl = "";
661
- containerGroupDsl += this.writeLine(`${group.canonicalName} = group "${group.name}" {`, level);
662
- group.getComponents().forEach((component) => {
663
- containerGroupDsl += this.writeComponent(component, level + 1);
802
+ const hasDirect = group.getComponents().length > 0;
803
+ if (hasDirect) {
804
+ containerGroupDsl += this.writeLine(`${group.canonicalName} = group "${group.dslName}" {`, level);
805
+ group.getComponents().forEach((component) => {
806
+ containerGroupDsl += this.writeComponent(component, level + 1);
807
+ });
808
+ containerGroupDsl += this.writeLine(`}`, level);
809
+ }
810
+ group.getGroups().forEach((nested) => {
811
+ containerGroupDsl += this.writeContainerGroup(nested, level);
664
812
  });
665
- containerGroupDsl += this.writeLine(`}`, level);
666
813
  return containerGroupDsl;
667
814
  }
668
815
  writeContainer(container, level) {
@@ -682,11 +829,17 @@ var StructurizrDSLWriter = class {
682
829
  }
683
830
  writeSoftwareSystemGroup(group, level) {
684
831
  let softwareSystemGroupDsl = "";
685
- softwareSystemGroupDsl += this.writeLine(`${group.canonicalName} = group "${group.name}" {`, level);
686
- group.getContainers().forEach((container) => {
687
- softwareSystemGroupDsl += this.writeContainer(container, level + 1);
832
+ const hasDirect = group.getContainers().length > 0;
833
+ if (hasDirect) {
834
+ softwareSystemGroupDsl += this.writeLine(`${group.canonicalName} = group "${group.dslName}" {`, level);
835
+ group.getContainers().forEach((container) => {
836
+ softwareSystemGroupDsl += this.writeContainer(container, level + 1);
837
+ });
838
+ softwareSystemGroupDsl += this.writeLine(`}`, level);
839
+ }
840
+ group.getGroups().forEach((nested) => {
841
+ softwareSystemGroupDsl += this.writeSoftwareSystemGroup(nested, level);
688
842
  });
689
- softwareSystemGroupDsl += this.writeLine(`}`, level);
690
843
  return softwareSystemGroupDsl;
691
844
  }
692
845
  writeSoftwareSystem(softwareSystem, level) {
@@ -704,7 +857,7 @@ var StructurizrDSLWriter = class {
704
857
  writeRelationship(relationship, level) {
705
858
  let dsl = "";
706
859
  if (relationship.archetype) {
707
- const arrow = `--${relationship.archetype.name}->`;
860
+ const arrow = `--${relationship.archetype.canonicalName}->`;
708
861
  const ovr = relationship.overrideDefinition;
709
862
  const desc = ovr?.description ?? relationship.description ?? "uses";
710
863
  dsl += this.writeLine(
@@ -738,21 +891,51 @@ var StructurizrDSLWriter = class {
738
891
  });
739
892
  return relationshipsDsl;
740
893
  }
894
+ hasNestedModelGroups(groups) {
895
+ return groups.some((g) => g.getGroups().length > 0 || this.hasNestedModelGroups(g.getGroups()));
896
+ }
897
+ hasNestedSoftwareSystemGroups(groups) {
898
+ return groups.some((g) => g.getGroups().length > 0 || this.hasNestedSoftwareSystemGroups(g.getGroups()));
899
+ }
900
+ hasNestedContainerGroups(groups) {
901
+ return groups.some((g) => g.getGroups().length > 0 || this.hasNestedContainerGroups(g.getGroups()));
902
+ }
903
+ hasNestedGroups() {
904
+ if (this.hasNestedModelGroups(this.model.getGroups())) return true;
905
+ for (const ss of this.model.getSoftwareSystems()) {
906
+ if (this.hasNestedSoftwareSystemGroups(ss.getGroups())) return true;
907
+ for (const c of ss.getGroups().flatMap((g) => g.getAllContainers())) {
908
+ if (this.hasNestedContainerGroups(c.getGroups())) return true;
909
+ }
910
+ }
911
+ return false;
912
+ }
741
913
  writeModelGroup(group, level) {
742
914
  let modelGroupDsl = "";
743
- modelGroupDsl += this.writeLine(`${group.canonicalName} = group "${group.name}" {`, level);
744
- group.getPeople().forEach((person) => {
745
- modelGroupDsl += this.writeElement("person", person, level + 1);
746
- });
747
- group.getSoftwareSystems().forEach((softwareSystem) => {
748
- modelGroupDsl += this.writeSoftwareSystem(softwareSystem, level + 1);
915
+ const hasDirect = group.getPeople().length > 0 || group.getSoftwareSystems().length > 0;
916
+ if (hasDirect) {
917
+ modelGroupDsl += this.writeLine(`${group.canonicalName} = group "${group.dslName}" {`, level);
918
+ group.getPeople().forEach((person) => {
919
+ modelGroupDsl += this.writeElement("person", person, level + 1);
920
+ });
921
+ group.getSoftwareSystems().forEach((softwareSystem) => {
922
+ modelGroupDsl += this.writeSoftwareSystem(softwareSystem, level + 1);
923
+ });
924
+ modelGroupDsl += this.writeLine(`}`, level);
925
+ }
926
+ group.getGroups().forEach((nested) => {
927
+ modelGroupDsl += this.writeModelGroup(nested, level);
749
928
  });
750
- modelGroupDsl += this.writeLine(`}`, level);
751
929
  return modelGroupDsl;
752
930
  }
753
931
  writeModel(model, level) {
754
932
  let modelDsl = "";
755
933
  modelDsl += this.writeLine(`model {`, level);
934
+ if (this.hasNestedGroups()) {
935
+ modelDsl += this.writeLine(`properties {`, level + 1);
936
+ modelDsl += this.writeLine(`"structurizr.groupSeparator" "/"`, level + 2);
937
+ modelDsl += this.writeLine(`}`, level + 1);
938
+ }
756
939
  modelDsl += this.writeArchetypes(level + 1);
757
940
  modelDsl += this.writeLine("// Elements", level + 1);
758
941
  model.getPeopleNotInGroups().forEach((person) => {
@@ -775,12 +958,66 @@ var StructurizrDSLWriter = class {
775
958
  if (view.title) {
776
959
  viewDsl += this.writeLine(`title "${view.title}"`, level + 1);
777
960
  }
961
+ if (view.isDefault) {
962
+ viewDsl += this.writeLine("default", level + 1);
963
+ }
778
964
  view.scopes.forEach((scope) => {
779
965
  viewDsl += this.writeLine(`${scope}`, level + 1);
780
966
  });
967
+ if (view.autoLayoutConfig) {
968
+ const { direction, rankSeparation, nodeSeparation } = view.autoLayoutConfig;
969
+ let line = "autoLayout";
970
+ if (direction) line += ` ${direction}`;
971
+ if (rankSeparation !== void 0) line += ` ${rankSeparation}`;
972
+ if (nodeSeparation !== void 0) line += ` ${nodeSeparation}`;
973
+ viewDsl += this.writeLine(line, level + 1);
974
+ }
975
+ if (view.properties.size > 0) {
976
+ viewDsl += this.writeLine("properties {", level + 1);
977
+ for (const [name, value] of view.properties) {
978
+ viewDsl += this.writeLine(`"${name}" "${value}"`, level + 2);
979
+ }
980
+ viewDsl += this.writeLine("}", level + 1);
981
+ }
781
982
  viewDsl += this.writeLine(`}`, level);
782
983
  return viewDsl;
783
984
  }
985
+ writeStyles(views, level) {
986
+ const { elementStyles, relationshipStyles } = views;
987
+ if (elementStyles.length === 0 && relationshipStyles.length === 0) return "";
988
+ let dsl = this.writeLine("styles {", level);
989
+ for (const { tag, definition: d } of elementStyles) {
990
+ dsl += this.writeLine(`element "${tag}" {`, level + 1);
991
+ if (d.shape) dsl += this.writeLine(`shape ${d.shape}`, level + 2);
992
+ if (d.icon) dsl += this.writeLine(`icon "${d.icon}"`, level + 2);
993
+ if (d.width) dsl += this.writeLine(`width ${d.width}`, level + 2);
994
+ if (d.height) dsl += this.writeLine(`height ${d.height}`, level + 2);
995
+ if (d.background) dsl += this.writeLine(`background "${d.background}"`, level + 2);
996
+ if (d.color) dsl += this.writeLine(`color "${d.color}"`, level + 2);
997
+ if (d.stroke) dsl += this.writeLine(`stroke "${d.stroke}"`, level + 2);
998
+ if (d.strokeWidth) dsl += this.writeLine(`strokeWidth ${d.strokeWidth}`, level + 2);
999
+ if (d.fontSize) dsl += this.writeLine(`fontSize ${d.fontSize}`, level + 2);
1000
+ if (d.border) dsl += this.writeLine(`border ${d.border}`, level + 2);
1001
+ if (d.opacity !== void 0) dsl += this.writeLine(`opacity ${d.opacity}`, level + 2);
1002
+ if (d.metadata !== void 0) dsl += this.writeLine(`metadata ${d.metadata}`, level + 2);
1003
+ if (d.description !== void 0) dsl += this.writeLine(`description ${d.description}`, level + 2);
1004
+ dsl += this.writeLine("}", level + 1);
1005
+ }
1006
+ for (const { tag, definition: d } of relationshipStyles) {
1007
+ dsl += this.writeLine(`relationship "${tag}" {`, level + 1);
1008
+ if (d.thickness) dsl += this.writeLine(`thickness ${d.thickness}`, level + 2);
1009
+ if (d.color) dsl += this.writeLine(`color "${d.color}"`, level + 2);
1010
+ if (d.style) dsl += this.writeLine(`style ${d.style}`, level + 2);
1011
+ if (d.routing) dsl += this.writeLine(`routing ${d.routing}`, level + 2);
1012
+ if (d.fontSize) dsl += this.writeLine(`fontSize ${d.fontSize}`, level + 2);
1013
+ if (d.width) dsl += this.writeLine(`width ${d.width}`, level + 2);
1014
+ if (d.position !== void 0) dsl += this.writeLine(`position ${d.position}`, level + 2);
1015
+ if (d.opacity !== void 0) dsl += this.writeLine(`opacity ${d.opacity}`, level + 2);
1016
+ dsl += this.writeLine("}", level + 1);
1017
+ }
1018
+ dsl += this.writeLine("}", level);
1019
+ return dsl;
1020
+ }
784
1021
  writeViews(views, level) {
785
1022
  let viewDsl = "";
786
1023
  viewDsl += this.writeLine(`views {`, level);
@@ -800,6 +1037,19 @@ var StructurizrDSLWriter = class {
800
1037
  views.componentViews.forEach((view) => {
801
1038
  viewDsl += this.writeView(view, "component", level + 1);
802
1039
  });
1040
+ viewDsl += this.writeStyles(views, level + 1);
1041
+ if (views.themes.length === 1) {
1042
+ viewDsl += this.writeLine(`theme ${views.themes[0]}`, level + 1);
1043
+ } else if (views.themes.length > 1) {
1044
+ viewDsl += this.writeLine(`themes ${views.themes.join(" ")}`, level + 1);
1045
+ }
1046
+ if (views.properties.size > 0) {
1047
+ viewDsl += this.writeLine("properties {", level + 1);
1048
+ for (const [name, value] of views.properties) {
1049
+ viewDsl += this.writeLine(`"${name}" "${value}"`, level + 2);
1050
+ }
1051
+ viewDsl += this.writeLine("}", level + 1);
1052
+ }
803
1053
  viewDsl += this.writeLine(`}`, level);
804
1054
  return viewDsl;
805
1055
  }
@@ -824,8 +1074,8 @@ import { glob } from "glob";
824
1074
  import { join, dirname } from "path";
825
1075
  import { fileURLToPath } from "url";
826
1076
  var _dirname = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLToPath(import.meta.url));
827
- async function buildModelWithCatalog(options = {}) {
828
- const { modelName = "model", modules: explicitModules, archetypes = {} } = options;
1077
+ async function buildModel(options = {}) {
1078
+ const { modelName = "model", modules: explicitModules, archetypes = {}, addViews } = options;
829
1079
  const model = new Model(modelName);
830
1080
  let c4Modules;
831
1081
  if (explicitModules) {
@@ -849,14 +1099,16 @@ async function buildModelWithCatalog(options = {}) {
849
1099
  rootCatalog[instance.key] = local;
850
1100
  registrations.push({ instance, key: instance.key, local });
851
1101
  }
1102
+ const dependenciesFor = (key) => Object.fromEntries(Object.entries(rootCatalog).filter(([k]) => k !== key));
852
1103
  for (const { instance, key, local } of registrations) {
853
- const dependencies = Object.fromEntries(Object.entries(rootCatalog).filter(([k]) => k !== key));
854
- instance.buildRelationships(local, dependencies, archetypes);
1104
+ instance.addRelationships(local, dependenciesFor(key), archetypes);
855
1105
  }
856
- return { model, catalog: rootCatalog };
857
- }
858
- async function buildModel(options = {}) {
859
- return (await buildModelWithCatalog(options)).model;
1106
+ const views = new Views();
1107
+ addViews?.(views, rootCatalog);
1108
+ for (const { instance, key, local } of registrations) {
1109
+ instance.addViews?.(views, local, dependenciesFor(key));
1110
+ }
1111
+ return { model, catalog: rootCatalog, views };
860
1112
  }
861
1113
 
862
1114
  // libs/c4-model/src/generateDiagrams.ts
@@ -865,9 +1117,8 @@ import * as os from "os";
865
1117
  import * as path from "path";
866
1118
  import { GenericContainer, Wait } from "testcontainers";
867
1119
  async function generateDiagrams(options) {
868
- const { views: viewsFactory, outputDir, ...buildOptions } = options;
869
- const { model, catalog } = await buildModelWithCatalog(buildOptions);
870
- const views = viewsFactory(catalog);
1120
+ const { outputDir, ...buildOptions } = options;
1121
+ const { model, views } = await buildModel(buildOptions);
871
1122
  const dsl = new StructurizrDSLWriter(model, views).write();
872
1123
  const tmpDir = await fs.promises.mkdtemp(path.join(fs.realpathSync(os.tmpdir()), "c4-diagrams-"));
873
1124
  await fs.promises.writeFile(path.join(tmpDir, "workspace.dsl"), dsl, "utf8");
@@ -888,10 +1139,65 @@ async function generateDiagrams(options) {
888
1139
  }
889
1140
  return generatedFiles;
890
1141
  }
1142
+
1143
+ // libs/c4-model/src/validateModel.ts
1144
+ import * as fs2 from "fs";
1145
+ import * as os2 from "os";
1146
+ import * as path2 from "path";
1147
+ import { GenericContainer as GenericContainer2, Wait as Wait2 } from "testcontainers";
1148
+ async function validateModel(model, views) {
1149
+ const dsl = new StructurizrDSLWriter(model, views).write();
1150
+ const tmpDir = await fs2.promises.mkdtemp(path2.join(fs2.realpathSync(os2.tmpdir()), "c4-validate-"));
1151
+ try {
1152
+ await fs2.promises.writeFile(path2.join(tmpDir, "workspace.dsl"), dsl, "utf8");
1153
+ const logs = [];
1154
+ try {
1155
+ await new GenericContainer2("structurizr/structurizr").withBindMounts([{ source: tmpDir, target: "/workspace", mode: "rw" }]).withCommand(["validate", "-workspace", "/workspace/workspace.dsl"]).withWaitStrategy(Wait2.forOneShotStartup()).withLogConsumer((stream) => stream.on("data", (chunk) => logs.push(chunk.toString()))).start();
1156
+ } catch {
1157
+ throw new Error(`Structurizr validation failed:
1158
+ ${logs.join("")}`);
1159
+ }
1160
+ } finally {
1161
+ await fs2.promises.rm(tmpDir, { recursive: true, force: true });
1162
+ }
1163
+ }
1164
+
1165
+ // libs/c4-model/src/exportWorkspaceJson.ts
1166
+ import * as fs3 from "fs";
1167
+ import * as os3 from "os";
1168
+ import * as path3 from "path";
1169
+ import { GenericContainer as GenericContainer3, Wait as Wait3 } from "testcontainers";
1170
+ async function exportWorkspaceJsonFromDsl(dsl) {
1171
+ const tmpDir = await fs3.promises.mkdtemp(path3.join(fs3.realpathSync(os3.tmpdir()), "c4-export-"));
1172
+ try {
1173
+ await fs3.promises.writeFile(path3.join(tmpDir, "workspace.dsl"), dsl, "utf8");
1174
+ const logs = [];
1175
+ try {
1176
+ await new GenericContainer3("structurizr/structurizr").withBindMounts([{ source: tmpDir, target: "/workspace", mode: "rw" }]).withCommand(["export", "-w", "/workspace/workspace.dsl", "-f", "json", "-o", "/workspace"]).withWaitStrategy(Wait3.forOneShotStartup()).withLogConsumer((stream) => stream.on("data", (chunk) => logs.push(chunk.toString()))).start();
1177
+ } catch {
1178
+ throw new Error(`Structurizr JSON export failed:
1179
+ ${logs.join("")}`);
1180
+ }
1181
+ const files = await fs3.promises.readdir(tmpDir);
1182
+ const jsonFile = files.find((f) => f.endsWith(".json"));
1183
+ if (!jsonFile) {
1184
+ throw new Error(`Structurizr JSON export produced no .json file. Files: ${files.join(", ")}`);
1185
+ }
1186
+ const jsonContent = await fs3.promises.readFile(path3.join(tmpDir, jsonFile), "utf8");
1187
+ return JSON.parse(jsonContent);
1188
+ } finally {
1189
+ await fs3.promises.rm(tmpDir, { recursive: true, force: true });
1190
+ }
1191
+ }
1192
+ async function exportWorkspaceJson(model, views) {
1193
+ const dsl = new StructurizrDSLWriter(model, views).write();
1194
+ return exportWorkspaceJsonFromDsl(dsl);
1195
+ }
891
1196
  export {
892
1197
  Component,
893
1198
  Container,
894
1199
  ContainerGroup,
1200
+ ELEMENT_KINDS,
895
1201
  ElementArchetype,
896
1202
  Model,
897
1203
  ModelGroup,
@@ -903,7 +1209,9 @@ export {
903
1209
  View,
904
1210
  Views,
905
1211
  buildModel,
906
- buildModelWithCatalog,
1212
+ exportWorkspaceJson,
1213
+ exportWorkspaceJsonFromDsl,
907
1214
  generateDiagrams,
908
- mergeArchetypeWithOverride
1215
+ mergeArchetypeWithOverride,
1216
+ validateModel
909
1217
  };