@agilewallaby/c4-model 2.7.0 → 2.8.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.cjs CHANGED
@@ -44,9 +44,11 @@ __export(index_exports, {
44
44
  View: () => View,
45
45
  Views: () => Views,
46
46
  buildModel: () => buildModel,
47
- buildModelWithCatalog: () => buildModelWithCatalog,
47
+ exportWorkspaceJson: () => exportWorkspaceJson,
48
+ exportWorkspaceJsonFromDsl: () => exportWorkspaceJsonFromDsl,
48
49
  generateDiagrams: () => generateDiagrams,
49
- mergeArchetypeWithOverride: () => mergeArchetypeWithOverride
50
+ mergeArchetypeWithOverride: () => mergeArchetypeWithOverride,
51
+ validateModel: () => validateModel
50
52
  });
51
53
  module.exports = __toCommonJS(index_exports);
52
54
 
@@ -221,9 +223,9 @@ var Element = class {
221
223
  getRelationshipsInHierarchy() {
222
224
  return this._relationships.concat(this.getChildElements().flatMap((element) => element.getRelationshipsInHierarchy()));
223
225
  }
224
- getChildElementNames(path2) {
226
+ getChildElementNames(path4) {
225
227
  const result = Array.from(this.getChildElements()).flatMap((reference) => {
226
- const currentPath = `${path2 ? path2 : "" + this.name}.${reference.name}`;
228
+ const currentPath = `${path4 ? path4 : "" + this.name}.${reference.name}`;
227
229
  return [currentPath, ...reference.getChildElementNames(currentPath)];
228
230
  });
229
231
  return result;
@@ -279,21 +281,46 @@ var Component = class extends TechnicalElement {
279
281
  };
280
282
 
281
283
  // libs/c4-model/src/container.ts
282
- var ContainerGroup = class extends Group {
283
- constructor(name, container) {
284
+ var ContainerGroup = class _ContainerGroup extends Group {
285
+ constructor(name, container, pathSegments = []) {
284
286
  super(name);
285
287
  this.name = name;
286
288
  this.container = container;
289
+ this.pathSegments = pathSegments;
287
290
  }
288
291
  _components = /* @__PURE__ */ new Map();
292
+ _groups = /* @__PURE__ */ new Map();
293
+ get canonicalName() {
294
+ return camelCase([...this.pathSegments, this.name].join(" "));
295
+ }
296
+ get dslName() {
297
+ return [...this.pathSegments, this.name].join("/");
298
+ }
289
299
  component(name, archetypeOrDef, override) {
290
300
  const component = this.container.component(name, archetypeOrDef, override);
291
301
  this._components.set(name, component);
292
302
  return component;
293
303
  }
304
+ group(groupName) {
305
+ let group = this._groups.get(groupName);
306
+ if (!group) {
307
+ group = new _ContainerGroup(groupName, this.container, [...this.pathSegments, this.name]);
308
+ this._groups.set(groupName, group);
309
+ }
310
+ return group;
311
+ }
312
+ getGroups() {
313
+ return Array.from(this._groups.values());
314
+ }
294
315
  getComponents() {
295
316
  return Array.from(this._components.values());
296
317
  }
318
+ getAllComponents() {
319
+ return [
320
+ ...this._components.values(),
321
+ ...Array.from(this._groups.values()).flatMap((g) => g.getAllComponents())
322
+ ];
323
+ }
297
324
  };
298
325
  var Container = class extends TechnicalElement {
299
326
  constructor(name, definition, archetype, overrideDefinition) {
@@ -330,7 +357,7 @@ var Container = class extends TechnicalElement {
330
357
  return Array.from(this._groups.values());
331
358
  }
332
359
  getComponentsNotInGroups() {
333
- const componentsInGroups = this.getGroups().flatMap((group) => group.getComponents());
360
+ const componentsInGroups = this.getGroups().flatMap((group) => group.getAllComponents());
334
361
  return Array.from(this._components.values()).filter((component) => !componentsInGroups.includes(component));
335
362
  }
336
363
  getChildElements() {
@@ -339,21 +366,46 @@ var Container = class extends TechnicalElement {
339
366
  };
340
367
 
341
368
  // libs/c4-model/src/softwareSystem.ts
342
- var SoftwareSystemGroup = class extends Group {
343
- constructor(name, softwareSystem) {
369
+ var SoftwareSystemGroup = class _SoftwareSystemGroup extends Group {
370
+ constructor(name, softwareSystem, pathSegments = []) {
344
371
  super(name);
345
372
  this.name = name;
346
373
  this.softwareSystem = softwareSystem;
374
+ this.pathSegments = pathSegments;
347
375
  }
348
376
  _containers = /* @__PURE__ */ new Map();
377
+ _groups = /* @__PURE__ */ new Map();
378
+ get canonicalName() {
379
+ return camelCase([...this.pathSegments, this.name].join(" "));
380
+ }
381
+ get dslName() {
382
+ return [...this.pathSegments, this.name].join("/");
383
+ }
349
384
  container(name, archetypeOrDef, override) {
350
385
  const container = this.softwareSystem.container(name, archetypeOrDef, override);
351
386
  this._containers.set(name, container);
352
387
  return container;
353
388
  }
389
+ group(groupName) {
390
+ let group = this._groups.get(groupName);
391
+ if (!group) {
392
+ group = new _SoftwareSystemGroup(groupName, this.softwareSystem, [...this.pathSegments, this.name]);
393
+ this._groups.set(groupName, group);
394
+ }
395
+ return group;
396
+ }
397
+ getGroups() {
398
+ return Array.from(this._groups.values());
399
+ }
354
400
  getContainers() {
355
401
  return Array.from(this._containers.values());
356
402
  }
403
+ getAllContainers() {
404
+ return [
405
+ ...this._containers.values(),
406
+ ...Array.from(this._groups.values()).flatMap((g) => g.getAllContainers())
407
+ ];
408
+ }
357
409
  };
358
410
  var SoftwareSystem = class extends Element {
359
411
  constructor(name, definition, archetype, overrideDefinition) {
@@ -393,7 +445,7 @@ var SoftwareSystem = class extends Element {
393
445
  return Array.from(this._containers.values());
394
446
  }
395
447
  getContainersNotInGroups() {
396
- const containersInGroups = Array.from(this._groups.values()).flatMap((group) => group.getContainers());
448
+ const containersInGroups = Array.from(this._groups.values()).flatMap((group) => group.getAllContainers());
397
449
  return Array.from(this._containers.values()).filter((container) => !containersInGroups.includes(container));
398
450
  }
399
451
  };
@@ -410,14 +462,22 @@ var Person = class extends Element {
410
462
  };
411
463
 
412
464
  // libs/c4-model/src/model.ts
413
- var ModelGroup = class extends Group {
414
- constructor(name, model) {
465
+ var ModelGroup = class _ModelGroup extends Group {
466
+ constructor(name, model, pathSegments = []) {
415
467
  super(name);
416
468
  this.name = name;
417
469
  this.model = model;
470
+ this.pathSegments = pathSegments;
418
471
  }
419
472
  softwareSystems = /* @__PURE__ */ new Map();
420
473
  people = /* @__PURE__ */ new Map();
474
+ _groups = /* @__PURE__ */ new Map();
475
+ get canonicalName() {
476
+ return camelCase([...this.pathSegments, this.name].join(" "));
477
+ }
478
+ get dslName() {
479
+ return [...this.pathSegments, this.name].join("/");
480
+ }
421
481
  softwareSystem(name, archetypeOrDef, override) {
422
482
  const softwareSystem = this.model.softwareSystem(name, archetypeOrDef, override);
423
483
  this.softwareSystems.set(name, softwareSystem);
@@ -428,12 +488,35 @@ var ModelGroup = class extends Group {
428
488
  this.people.set(name, person);
429
489
  return person;
430
490
  }
491
+ group(groupName) {
492
+ let group = this._groups.get(groupName);
493
+ if (!group) {
494
+ group = new _ModelGroup(groupName, this.model, [...this.pathSegments, this.name]);
495
+ this._groups.set(groupName, group);
496
+ }
497
+ return group;
498
+ }
499
+ getGroups() {
500
+ return Array.from(this._groups.values());
501
+ }
431
502
  getSoftwareSystems() {
432
503
  return Array.from(this.softwareSystems.values());
433
504
  }
434
505
  getPeople() {
435
506
  return Array.from(this.people.values());
436
507
  }
508
+ getAllSoftwareSystems() {
509
+ return [
510
+ ...this.softwareSystems.values(),
511
+ ...Array.from(this._groups.values()).flatMap((g) => g.getAllSoftwareSystems())
512
+ ];
513
+ }
514
+ getAllPeople() {
515
+ return [
516
+ ...this.people.values(),
517
+ ...Array.from(this._groups.values()).flatMap((g) => g.getAllPeople())
518
+ ];
519
+ }
437
520
  };
438
521
  var Model = class {
439
522
  constructor(name) {
@@ -458,7 +541,6 @@ var Model = class {
458
541
  this.softwareSystems.set(name, system);
459
542
  return system;
460
543
  }
461
- // TODO:Should be a Group<SoftwareSystem | Person> if that is added back in
462
544
  group(groupName) {
463
545
  let group = this.groups.get(groupName);
464
546
  if (!group) {
@@ -493,11 +575,11 @@ var Model = class {
493
575
  return Array.from(this.softwareSystems.values());
494
576
  }
495
577
  getPeopleNotInGroups() {
496
- const peopleInGroups = Array.from(this.groups.values()).flatMap((group) => group.getPeople());
578
+ const peopleInGroups = Array.from(this.groups.values()).flatMap((group) => group.getAllPeople());
497
579
  return Array.from(this.people.values()).filter((person) => !peopleInGroups.includes(person));
498
580
  }
499
581
  getSoftwareSystemsNotInGroups() {
500
- const systemsInGroups = Array.from(this.groups.values()).flatMap((group) => group.getSoftwareSystems());
582
+ const systemsInGroups = Array.from(this.groups.values()).flatMap((group) => group.getAllSoftwareSystems());
501
583
  return Array.from(this.softwareSystems.values()).filter((system) => !systemsInGroups.includes(system));
502
584
  }
503
585
  getGroups() {
@@ -517,6 +599,9 @@ var View = class {
517
599
  description;
518
600
  title;
519
601
  _scopes = [];
602
+ _autoLayout;
603
+ _isDefault = false;
604
+ _properties = /* @__PURE__ */ new Map();
520
605
  includeAll() {
521
606
  this._scopes.push("include *");
522
607
  }
@@ -535,15 +620,37 @@ var View = class {
535
620
  excludeExpression(expression) {
536
621
  this._scopes.push(`exclude ${expression}`);
537
622
  }
623
+ autoLayout(direction, rankSeparation, nodeSeparation) {
624
+ this._autoLayout = { direction, rankSeparation, nodeSeparation };
625
+ }
626
+ setDefault() {
627
+ this._isDefault = true;
628
+ }
629
+ addProperty(name, value) {
630
+ this._properties.set(name, value);
631
+ }
538
632
  get scopes() {
539
633
  return this._scopes;
540
634
  }
635
+ get autoLayoutConfig() {
636
+ return this._autoLayout;
637
+ }
638
+ get isDefault() {
639
+ return this._isDefault;
640
+ }
641
+ get properties() {
642
+ return this._properties;
643
+ }
541
644
  };
542
645
  var Views = class {
543
646
  _systemLandscapeViews = /* @__PURE__ */ new Map();
544
647
  _systemContextViews = /* @__PURE__ */ new Map();
545
648
  _containerViews = /* @__PURE__ */ new Map();
546
649
  _componentViews = /* @__PURE__ */ new Map();
650
+ _elementStyles = [];
651
+ _relationshipStyles = [];
652
+ _themes = [];
653
+ _properties = /* @__PURE__ */ new Map();
547
654
  addSystemLandscapeView(key, definition) {
548
655
  const view = new View(key, { subject: void 0, description: definition.description, title: definition.title });
549
656
  this._systemLandscapeViews.set(key, view);
@@ -564,6 +671,18 @@ var Views = class {
564
671
  this._componentViews.set(key, view);
565
672
  return view;
566
673
  }
674
+ addElementStyle(tag, definition) {
675
+ this._elementStyles.push({ tag, definition });
676
+ }
677
+ addRelationshipStyle(tag, definition) {
678
+ this._relationshipStyles.push({ tag, definition });
679
+ }
680
+ addTheme(url) {
681
+ this._themes.push(url);
682
+ }
683
+ addProperty(name, value) {
684
+ this._properties.set(name, value);
685
+ }
567
686
  get systemLandscapeViews() {
568
687
  return Array.from(this._systemLandscapeViews.values());
569
688
  }
@@ -576,6 +695,18 @@ var Views = class {
576
695
  get componentViews() {
577
696
  return Array.from(this._componentViews.values());
578
697
  }
698
+ get elementStyles() {
699
+ return this._elementStyles;
700
+ }
701
+ get relationshipStyles() {
702
+ return this._relationshipStyles;
703
+ }
704
+ get themes() {
705
+ return this._themes;
706
+ }
707
+ get properties() {
708
+ return this._properties;
709
+ }
579
710
  };
580
711
 
581
712
  // libs/c4-model/src/structurizrDslWriter.ts
@@ -710,11 +841,17 @@ var StructurizrDSLWriter = class {
710
841
  }
711
842
  writeContainerGroup(group, level) {
712
843
  let containerGroupDsl = "";
713
- containerGroupDsl += this.writeLine(`${group.canonicalName} = group "${group.name}" {`, level);
714
- group.getComponents().forEach((component) => {
715
- containerGroupDsl += this.writeComponent(component, level + 1);
844
+ const hasDirect = group.getComponents().length > 0;
845
+ if (hasDirect) {
846
+ containerGroupDsl += this.writeLine(`${group.canonicalName} = group "${group.dslName}" {`, level);
847
+ group.getComponents().forEach((component) => {
848
+ containerGroupDsl += this.writeComponent(component, level + 1);
849
+ });
850
+ containerGroupDsl += this.writeLine(`}`, level);
851
+ }
852
+ group.getGroups().forEach((nested) => {
853
+ containerGroupDsl += this.writeContainerGroup(nested, level);
716
854
  });
717
- containerGroupDsl += this.writeLine(`}`, level);
718
855
  return containerGroupDsl;
719
856
  }
720
857
  writeContainer(container, level) {
@@ -734,11 +871,17 @@ var StructurizrDSLWriter = class {
734
871
  }
735
872
  writeSoftwareSystemGroup(group, level) {
736
873
  let softwareSystemGroupDsl = "";
737
- softwareSystemGroupDsl += this.writeLine(`${group.canonicalName} = group "${group.name}" {`, level);
738
- group.getContainers().forEach((container) => {
739
- softwareSystemGroupDsl += this.writeContainer(container, level + 1);
874
+ const hasDirect = group.getContainers().length > 0;
875
+ if (hasDirect) {
876
+ softwareSystemGroupDsl += this.writeLine(`${group.canonicalName} = group "${group.dslName}" {`, level);
877
+ group.getContainers().forEach((container) => {
878
+ softwareSystemGroupDsl += this.writeContainer(container, level + 1);
879
+ });
880
+ softwareSystemGroupDsl += this.writeLine(`}`, level);
881
+ }
882
+ group.getGroups().forEach((nested) => {
883
+ softwareSystemGroupDsl += this.writeSoftwareSystemGroup(nested, level);
740
884
  });
741
- softwareSystemGroupDsl += this.writeLine(`}`, level);
742
885
  return softwareSystemGroupDsl;
743
886
  }
744
887
  writeSoftwareSystem(softwareSystem, level) {
@@ -790,21 +933,51 @@ var StructurizrDSLWriter = class {
790
933
  });
791
934
  return relationshipsDsl;
792
935
  }
936
+ hasNestedModelGroups(groups) {
937
+ return groups.some((g) => g.getGroups().length > 0 || this.hasNestedModelGroups(g.getGroups()));
938
+ }
939
+ hasNestedSoftwareSystemGroups(groups) {
940
+ return groups.some((g) => g.getGroups().length > 0 || this.hasNestedSoftwareSystemGroups(g.getGroups()));
941
+ }
942
+ hasNestedContainerGroups(groups) {
943
+ return groups.some((g) => g.getGroups().length > 0 || this.hasNestedContainerGroups(g.getGroups()));
944
+ }
945
+ hasNestedGroups() {
946
+ if (this.hasNestedModelGroups(this.model.getGroups())) return true;
947
+ for (const ss of this.model.getSoftwareSystems()) {
948
+ if (this.hasNestedSoftwareSystemGroups(ss.getGroups())) return true;
949
+ for (const c of ss.getGroups().flatMap((g) => g.getAllContainers())) {
950
+ if (this.hasNestedContainerGroups(c.getGroups())) return true;
951
+ }
952
+ }
953
+ return false;
954
+ }
793
955
  writeModelGroup(group, level) {
794
956
  let modelGroupDsl = "";
795
- modelGroupDsl += this.writeLine(`${group.canonicalName} = group "${group.name}" {`, level);
796
- group.getPeople().forEach((person) => {
797
- modelGroupDsl += this.writeElement("person", person, level + 1);
798
- });
799
- group.getSoftwareSystems().forEach((softwareSystem) => {
800
- modelGroupDsl += this.writeSoftwareSystem(softwareSystem, level + 1);
957
+ const hasDirect = group.getPeople().length > 0 || group.getSoftwareSystems().length > 0;
958
+ if (hasDirect) {
959
+ modelGroupDsl += this.writeLine(`${group.canonicalName} = group "${group.dslName}" {`, level);
960
+ group.getPeople().forEach((person) => {
961
+ modelGroupDsl += this.writeElement("person", person, level + 1);
962
+ });
963
+ group.getSoftwareSystems().forEach((softwareSystem) => {
964
+ modelGroupDsl += this.writeSoftwareSystem(softwareSystem, level + 1);
965
+ });
966
+ modelGroupDsl += this.writeLine(`}`, level);
967
+ }
968
+ group.getGroups().forEach((nested) => {
969
+ modelGroupDsl += this.writeModelGroup(nested, level);
801
970
  });
802
- modelGroupDsl += this.writeLine(`}`, level);
803
971
  return modelGroupDsl;
804
972
  }
805
973
  writeModel(model, level) {
806
974
  let modelDsl = "";
807
975
  modelDsl += this.writeLine(`model {`, level);
976
+ if (this.hasNestedGroups()) {
977
+ modelDsl += this.writeLine(`properties {`, level + 1);
978
+ modelDsl += this.writeLine(`"structurizr.groupSeparator" "/"`, level + 2);
979
+ modelDsl += this.writeLine(`}`, level + 1);
980
+ }
808
981
  modelDsl += this.writeArchetypes(level + 1);
809
982
  modelDsl += this.writeLine("// Elements", level + 1);
810
983
  model.getPeopleNotInGroups().forEach((person) => {
@@ -827,12 +1000,66 @@ var StructurizrDSLWriter = class {
827
1000
  if (view.title) {
828
1001
  viewDsl += this.writeLine(`title "${view.title}"`, level + 1);
829
1002
  }
1003
+ if (view.isDefault) {
1004
+ viewDsl += this.writeLine("default", level + 1);
1005
+ }
830
1006
  view.scopes.forEach((scope) => {
831
1007
  viewDsl += this.writeLine(`${scope}`, level + 1);
832
1008
  });
1009
+ if (view.autoLayoutConfig) {
1010
+ const { direction, rankSeparation, nodeSeparation } = view.autoLayoutConfig;
1011
+ let line = "autoLayout";
1012
+ if (direction) line += ` ${direction}`;
1013
+ if (rankSeparation !== void 0) line += ` ${rankSeparation}`;
1014
+ if (nodeSeparation !== void 0) line += ` ${nodeSeparation}`;
1015
+ viewDsl += this.writeLine(line, level + 1);
1016
+ }
1017
+ if (view.properties.size > 0) {
1018
+ viewDsl += this.writeLine("properties {", level + 1);
1019
+ for (const [name, value] of view.properties) {
1020
+ viewDsl += this.writeLine(`"${name}" "${value}"`, level + 2);
1021
+ }
1022
+ viewDsl += this.writeLine("}", level + 1);
1023
+ }
833
1024
  viewDsl += this.writeLine(`}`, level);
834
1025
  return viewDsl;
835
1026
  }
1027
+ writeStyles(views, level) {
1028
+ const { elementStyles, relationshipStyles } = views;
1029
+ if (elementStyles.length === 0 && relationshipStyles.length === 0) return "";
1030
+ let dsl = this.writeLine("styles {", level);
1031
+ for (const { tag, definition: d } of elementStyles) {
1032
+ dsl += this.writeLine(`element "${tag}" {`, level + 1);
1033
+ if (d.shape) dsl += this.writeLine(`shape ${d.shape}`, level + 2);
1034
+ if (d.icon) dsl += this.writeLine(`icon "${d.icon}"`, level + 2);
1035
+ if (d.width) dsl += this.writeLine(`width ${d.width}`, level + 2);
1036
+ if (d.height) dsl += this.writeLine(`height ${d.height}`, level + 2);
1037
+ if (d.background) dsl += this.writeLine(`background "${d.background}"`, level + 2);
1038
+ if (d.color) dsl += this.writeLine(`color "${d.color}"`, level + 2);
1039
+ if (d.stroke) dsl += this.writeLine(`stroke "${d.stroke}"`, level + 2);
1040
+ if (d.strokeWidth) dsl += this.writeLine(`strokeWidth ${d.strokeWidth}`, level + 2);
1041
+ if (d.fontSize) dsl += this.writeLine(`fontSize ${d.fontSize}`, level + 2);
1042
+ if (d.border) dsl += this.writeLine(`border ${d.border}`, level + 2);
1043
+ if (d.opacity !== void 0) dsl += this.writeLine(`opacity ${d.opacity}`, level + 2);
1044
+ if (d.metadata !== void 0) dsl += this.writeLine(`metadata ${d.metadata}`, level + 2);
1045
+ if (d.description !== void 0) dsl += this.writeLine(`description ${d.description}`, level + 2);
1046
+ dsl += this.writeLine("}", level + 1);
1047
+ }
1048
+ for (const { tag, definition: d } of relationshipStyles) {
1049
+ dsl += this.writeLine(`relationship "${tag}" {`, level + 1);
1050
+ if (d.thickness) dsl += this.writeLine(`thickness ${d.thickness}`, level + 2);
1051
+ if (d.color) dsl += this.writeLine(`color "${d.color}"`, level + 2);
1052
+ if (d.style) dsl += this.writeLine(`style ${d.style}`, level + 2);
1053
+ if (d.routing) dsl += this.writeLine(`routing ${d.routing}`, level + 2);
1054
+ if (d.fontSize) dsl += this.writeLine(`fontSize ${d.fontSize}`, level + 2);
1055
+ if (d.width) dsl += this.writeLine(`width ${d.width}`, level + 2);
1056
+ if (d.position !== void 0) dsl += this.writeLine(`position ${d.position}`, level + 2);
1057
+ if (d.opacity !== void 0) dsl += this.writeLine(`opacity ${d.opacity}`, level + 2);
1058
+ dsl += this.writeLine("}", level + 1);
1059
+ }
1060
+ dsl += this.writeLine("}", level);
1061
+ return dsl;
1062
+ }
836
1063
  writeViews(views, level) {
837
1064
  let viewDsl = "";
838
1065
  viewDsl += this.writeLine(`views {`, level);
@@ -852,6 +1079,19 @@ var StructurizrDSLWriter = class {
852
1079
  views.componentViews.forEach((view) => {
853
1080
  viewDsl += this.writeView(view, "component", level + 1);
854
1081
  });
1082
+ viewDsl += this.writeStyles(views, level + 1);
1083
+ if (views.themes.length === 1) {
1084
+ viewDsl += this.writeLine(`theme ${views.themes[0]}`, level + 1);
1085
+ } else if (views.themes.length > 1) {
1086
+ viewDsl += this.writeLine(`themes ${views.themes.join(" ")}`, level + 1);
1087
+ }
1088
+ if (views.properties.size > 0) {
1089
+ viewDsl += this.writeLine("properties {", level + 1);
1090
+ for (const [name, value] of views.properties) {
1091
+ viewDsl += this.writeLine(`"${name}" "${value}"`, level + 2);
1092
+ }
1093
+ viewDsl += this.writeLine("}", level + 1);
1094
+ }
855
1095
  viewDsl += this.writeLine(`}`, level);
856
1096
  return viewDsl;
857
1097
  }
@@ -877,8 +1117,8 @@ var import_path = require("path");
877
1117
  var import_url = require("url");
878
1118
  var import_meta = {};
879
1119
  var _dirname = typeof __dirname !== "undefined" ? __dirname : (0, import_path.dirname)((0, import_url.fileURLToPath)(import_meta.url));
880
- async function buildModelWithCatalog(options = {}) {
881
- const { modelName = "model", modules: explicitModules, archetypes = {} } = options;
1120
+ async function buildModel(options = {}) {
1121
+ const { modelName = "model", modules: explicitModules, archetypes = {}, addViews } = options;
882
1122
  const model = new Model(modelName);
883
1123
  let c4Modules;
884
1124
  if (explicitModules) {
@@ -902,14 +1142,16 @@ async function buildModelWithCatalog(options = {}) {
902
1142
  rootCatalog[instance.key] = local;
903
1143
  registrations.push({ instance, key: instance.key, local });
904
1144
  }
1145
+ const dependenciesFor = (key) => Object.fromEntries(Object.entries(rootCatalog).filter(([k]) => k !== key));
905
1146
  for (const { instance, key, local } of registrations) {
906
- const dependencies = Object.fromEntries(Object.entries(rootCatalog).filter(([k]) => k !== key));
907
- instance.buildRelationships(local, dependencies, archetypes);
1147
+ instance.addRelationships(local, dependenciesFor(key), archetypes);
908
1148
  }
909
- return { model, catalog: rootCatalog };
910
- }
911
- async function buildModel(options = {}) {
912
- return (await buildModelWithCatalog(options)).model;
1149
+ const views = new Views();
1150
+ addViews?.(views, rootCatalog);
1151
+ for (const { instance, key, local } of registrations) {
1152
+ instance.addViews?.(views, local, dependenciesFor(key));
1153
+ }
1154
+ return { model, catalog: rootCatalog, views };
913
1155
  }
914
1156
 
915
1157
  // libs/c4-model/src/generateDiagrams.ts
@@ -918,9 +1160,8 @@ var os = __toESM(require("os"), 1);
918
1160
  var path = __toESM(require("path"), 1);
919
1161
  var import_testcontainers = require("testcontainers");
920
1162
  async function generateDiagrams(options) {
921
- const { views: viewsFactory, outputDir, ...buildOptions } = options;
922
- const { model, catalog } = await buildModelWithCatalog(buildOptions);
923
- const views = viewsFactory(catalog);
1163
+ const { outputDir, ...buildOptions } = options;
1164
+ const { model, views } = await buildModel(buildOptions);
924
1165
  const dsl = new StructurizrDSLWriter(model, views).write();
925
1166
  const tmpDir = await fs.promises.mkdtemp(path.join(fs.realpathSync(os.tmpdir()), "c4-diagrams-"));
926
1167
  await fs.promises.writeFile(path.join(tmpDir, "workspace.dsl"), dsl, "utf8");
@@ -941,6 +1182,60 @@ async function generateDiagrams(options) {
941
1182
  }
942
1183
  return generatedFiles;
943
1184
  }
1185
+
1186
+ // libs/c4-model/src/validateModel.ts
1187
+ var fs2 = __toESM(require("fs"), 1);
1188
+ var os2 = __toESM(require("os"), 1);
1189
+ var path2 = __toESM(require("path"), 1);
1190
+ var import_testcontainers2 = require("testcontainers");
1191
+ async function validateModel(model, views) {
1192
+ const dsl = new StructurizrDSLWriter(model, views).write();
1193
+ const tmpDir = await fs2.promises.mkdtemp(path2.join(fs2.realpathSync(os2.tmpdir()), "c4-validate-"));
1194
+ try {
1195
+ await fs2.promises.writeFile(path2.join(tmpDir, "workspace.dsl"), dsl, "utf8");
1196
+ const logs = [];
1197
+ try {
1198
+ await new import_testcontainers2.GenericContainer("structurizr/structurizr").withBindMounts([{ source: tmpDir, target: "/workspace", mode: "rw" }]).withCommand(["validate", "-workspace", "/workspace/workspace.dsl"]).withWaitStrategy(import_testcontainers2.Wait.forOneShotStartup()).withLogConsumer((stream) => stream.on("data", (chunk) => logs.push(chunk.toString()))).start();
1199
+ } catch {
1200
+ throw new Error(`Structurizr validation failed:
1201
+ ${logs.join("")}`);
1202
+ }
1203
+ } finally {
1204
+ await fs2.promises.rm(tmpDir, { recursive: true, force: true });
1205
+ }
1206
+ }
1207
+
1208
+ // libs/c4-model/src/exportWorkspaceJson.ts
1209
+ var fs3 = __toESM(require("fs"), 1);
1210
+ var os3 = __toESM(require("os"), 1);
1211
+ var path3 = __toESM(require("path"), 1);
1212
+ var import_testcontainers3 = require("testcontainers");
1213
+ async function exportWorkspaceJsonFromDsl(dsl) {
1214
+ const tmpDir = await fs3.promises.mkdtemp(path3.join(fs3.realpathSync(os3.tmpdir()), "c4-export-"));
1215
+ try {
1216
+ await fs3.promises.writeFile(path3.join(tmpDir, "workspace.dsl"), dsl, "utf8");
1217
+ const logs = [];
1218
+ try {
1219
+ await new import_testcontainers3.GenericContainer("structurizr/structurizr").withBindMounts([{ source: tmpDir, target: "/workspace", mode: "rw" }]).withCommand(["export", "-w", "/workspace/workspace.dsl", "-f", "json", "-o", "/workspace"]).withWaitStrategy(import_testcontainers3.Wait.forOneShotStartup()).withLogConsumer((stream) => stream.on("data", (chunk) => logs.push(chunk.toString()))).start();
1220
+ } catch {
1221
+ throw new Error(`Structurizr JSON export failed:
1222
+ ${logs.join("")}`);
1223
+ }
1224
+ const files = await fs3.promises.readdir(tmpDir);
1225
+ const jsonFile = files.find((f) => f.endsWith(".json"));
1226
+ if (!jsonFile) {
1227
+ throw new Error(`Structurizr JSON export produced no .json file. Files: ${files.join(", ")}`);
1228
+ }
1229
+ const jsonContent = await fs3.promises.readFile(path3.join(tmpDir, jsonFile), "utf8");
1230
+ return JSON.parse(jsonContent);
1231
+ } finally {
1232
+ await fs3.promises.rm(tmpDir, { recursive: true, force: true });
1233
+ }
1234
+ }
1235
+ async function exportWorkspaceJson(model, views) {
1236
+ const dsl = new StructurizrDSLWriter(model, views).write();
1237
+ return exportWorkspaceJsonFromDsl(dsl);
1238
+ }
944
1239
  // Annotate the CommonJS export names for ESM import in node:
945
1240
  0 && (module.exports = {
946
1241
  Component,
@@ -957,7 +1252,9 @@ async function generateDiagrams(options) {
957
1252
  View,
958
1253
  Views,
959
1254
  buildModel,
960
- buildModelWithCatalog,
1255
+ exportWorkspaceJson,
1256
+ exportWorkspaceJsonFromDsl,
961
1257
  generateDiagrams,
962
- mergeArchetypeWithOverride
1258
+ mergeArchetypeWithOverride,
1259
+ validateModel
963
1260
  });