@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/package.json +1 -1
- package/src/buildModel.d.ts +7 -4
- package/src/container.d.ts +8 -1
- package/src/exportWorkspaceJson.d.ts +4 -0
- package/src/generateDiagrams.d.ts +1 -3
- package/src/index.cjs +340 -43
- package/src/index.d.ts +138 -45
- package/src/index.js +336 -41
- package/src/model.d.ts +13 -3
- package/src/softwareSystem.d.ts +8 -1
- package/src/structurizrDslWriter.d.ts +5 -0
- package/src/validateModel.d.ts +3 -0
- package/src/views.d.ts +63 -2
package/src/index.js
CHANGED
|
@@ -169,9 +169,9 @@ var Element = class {
|
|
|
169
169
|
getRelationshipsInHierarchy() {
|
|
170
170
|
return this._relationships.concat(this.getChildElements().flatMap((element) => element.getRelationshipsInHierarchy()));
|
|
171
171
|
}
|
|
172
|
-
getChildElementNames(
|
|
172
|
+
getChildElementNames(path4) {
|
|
173
173
|
const result = Array.from(this.getChildElements()).flatMap((reference) => {
|
|
174
|
-
const currentPath = `${
|
|
174
|
+
const currentPath = `${path4 ? path4 : "" + this.name}.${reference.name}`;
|
|
175
175
|
return [currentPath, ...reference.getChildElementNames(currentPath)];
|
|
176
176
|
});
|
|
177
177
|
return result;
|
|
@@ -227,21 +227,46 @@ var Component = class extends TechnicalElement {
|
|
|
227
227
|
};
|
|
228
228
|
|
|
229
229
|
// libs/c4-model/src/container.ts
|
|
230
|
-
var ContainerGroup = class extends Group {
|
|
231
|
-
constructor(name, container) {
|
|
230
|
+
var ContainerGroup = class _ContainerGroup extends Group {
|
|
231
|
+
constructor(name, container, pathSegments = []) {
|
|
232
232
|
super(name);
|
|
233
233
|
this.name = name;
|
|
234
234
|
this.container = container;
|
|
235
|
+
this.pathSegments = pathSegments;
|
|
235
236
|
}
|
|
236
237
|
_components = /* @__PURE__ */ new Map();
|
|
238
|
+
_groups = /* @__PURE__ */ new Map();
|
|
239
|
+
get canonicalName() {
|
|
240
|
+
return camelCase([...this.pathSegments, this.name].join(" "));
|
|
241
|
+
}
|
|
242
|
+
get dslName() {
|
|
243
|
+
return [...this.pathSegments, this.name].join("/");
|
|
244
|
+
}
|
|
237
245
|
component(name, archetypeOrDef, override) {
|
|
238
246
|
const component = this.container.component(name, archetypeOrDef, override);
|
|
239
247
|
this._components.set(name, component);
|
|
240
248
|
return component;
|
|
241
249
|
}
|
|
250
|
+
group(groupName) {
|
|
251
|
+
let group = this._groups.get(groupName);
|
|
252
|
+
if (!group) {
|
|
253
|
+
group = new _ContainerGroup(groupName, this.container, [...this.pathSegments, this.name]);
|
|
254
|
+
this._groups.set(groupName, group);
|
|
255
|
+
}
|
|
256
|
+
return group;
|
|
257
|
+
}
|
|
258
|
+
getGroups() {
|
|
259
|
+
return Array.from(this._groups.values());
|
|
260
|
+
}
|
|
242
261
|
getComponents() {
|
|
243
262
|
return Array.from(this._components.values());
|
|
244
263
|
}
|
|
264
|
+
getAllComponents() {
|
|
265
|
+
return [
|
|
266
|
+
...this._components.values(),
|
|
267
|
+
...Array.from(this._groups.values()).flatMap((g) => g.getAllComponents())
|
|
268
|
+
];
|
|
269
|
+
}
|
|
245
270
|
};
|
|
246
271
|
var Container = class extends TechnicalElement {
|
|
247
272
|
constructor(name, definition, archetype, overrideDefinition) {
|
|
@@ -278,7 +303,7 @@ var Container = class extends TechnicalElement {
|
|
|
278
303
|
return Array.from(this._groups.values());
|
|
279
304
|
}
|
|
280
305
|
getComponentsNotInGroups() {
|
|
281
|
-
const componentsInGroups = this.getGroups().flatMap((group) => group.
|
|
306
|
+
const componentsInGroups = this.getGroups().flatMap((group) => group.getAllComponents());
|
|
282
307
|
return Array.from(this._components.values()).filter((component) => !componentsInGroups.includes(component));
|
|
283
308
|
}
|
|
284
309
|
getChildElements() {
|
|
@@ -287,21 +312,46 @@ var Container = class extends TechnicalElement {
|
|
|
287
312
|
};
|
|
288
313
|
|
|
289
314
|
// libs/c4-model/src/softwareSystem.ts
|
|
290
|
-
var SoftwareSystemGroup = class extends Group {
|
|
291
|
-
constructor(name, softwareSystem) {
|
|
315
|
+
var SoftwareSystemGroup = class _SoftwareSystemGroup extends Group {
|
|
316
|
+
constructor(name, softwareSystem, pathSegments = []) {
|
|
292
317
|
super(name);
|
|
293
318
|
this.name = name;
|
|
294
319
|
this.softwareSystem = softwareSystem;
|
|
320
|
+
this.pathSegments = pathSegments;
|
|
295
321
|
}
|
|
296
322
|
_containers = /* @__PURE__ */ new Map();
|
|
323
|
+
_groups = /* @__PURE__ */ new Map();
|
|
324
|
+
get canonicalName() {
|
|
325
|
+
return camelCase([...this.pathSegments, this.name].join(" "));
|
|
326
|
+
}
|
|
327
|
+
get dslName() {
|
|
328
|
+
return [...this.pathSegments, this.name].join("/");
|
|
329
|
+
}
|
|
297
330
|
container(name, archetypeOrDef, override) {
|
|
298
331
|
const container = this.softwareSystem.container(name, archetypeOrDef, override);
|
|
299
332
|
this._containers.set(name, container);
|
|
300
333
|
return container;
|
|
301
334
|
}
|
|
335
|
+
group(groupName) {
|
|
336
|
+
let group = this._groups.get(groupName);
|
|
337
|
+
if (!group) {
|
|
338
|
+
group = new _SoftwareSystemGroup(groupName, this.softwareSystem, [...this.pathSegments, this.name]);
|
|
339
|
+
this._groups.set(groupName, group);
|
|
340
|
+
}
|
|
341
|
+
return group;
|
|
342
|
+
}
|
|
343
|
+
getGroups() {
|
|
344
|
+
return Array.from(this._groups.values());
|
|
345
|
+
}
|
|
302
346
|
getContainers() {
|
|
303
347
|
return Array.from(this._containers.values());
|
|
304
348
|
}
|
|
349
|
+
getAllContainers() {
|
|
350
|
+
return [
|
|
351
|
+
...this._containers.values(),
|
|
352
|
+
...Array.from(this._groups.values()).flatMap((g) => g.getAllContainers())
|
|
353
|
+
];
|
|
354
|
+
}
|
|
305
355
|
};
|
|
306
356
|
var SoftwareSystem = class extends Element {
|
|
307
357
|
constructor(name, definition, archetype, overrideDefinition) {
|
|
@@ -341,7 +391,7 @@ var SoftwareSystem = class extends Element {
|
|
|
341
391
|
return Array.from(this._containers.values());
|
|
342
392
|
}
|
|
343
393
|
getContainersNotInGroups() {
|
|
344
|
-
const containersInGroups = Array.from(this._groups.values()).flatMap((group) => group.
|
|
394
|
+
const containersInGroups = Array.from(this._groups.values()).flatMap((group) => group.getAllContainers());
|
|
345
395
|
return Array.from(this._containers.values()).filter((container) => !containersInGroups.includes(container));
|
|
346
396
|
}
|
|
347
397
|
};
|
|
@@ -358,14 +408,22 @@ var Person = class extends Element {
|
|
|
358
408
|
};
|
|
359
409
|
|
|
360
410
|
// libs/c4-model/src/model.ts
|
|
361
|
-
var ModelGroup = class extends Group {
|
|
362
|
-
constructor(name, model) {
|
|
411
|
+
var ModelGroup = class _ModelGroup extends Group {
|
|
412
|
+
constructor(name, model, pathSegments = []) {
|
|
363
413
|
super(name);
|
|
364
414
|
this.name = name;
|
|
365
415
|
this.model = model;
|
|
416
|
+
this.pathSegments = pathSegments;
|
|
366
417
|
}
|
|
367
418
|
softwareSystems = /* @__PURE__ */ new Map();
|
|
368
419
|
people = /* @__PURE__ */ new Map();
|
|
420
|
+
_groups = /* @__PURE__ */ new Map();
|
|
421
|
+
get canonicalName() {
|
|
422
|
+
return camelCase([...this.pathSegments, this.name].join(" "));
|
|
423
|
+
}
|
|
424
|
+
get dslName() {
|
|
425
|
+
return [...this.pathSegments, this.name].join("/");
|
|
426
|
+
}
|
|
369
427
|
softwareSystem(name, archetypeOrDef, override) {
|
|
370
428
|
const softwareSystem = this.model.softwareSystem(name, archetypeOrDef, override);
|
|
371
429
|
this.softwareSystems.set(name, softwareSystem);
|
|
@@ -376,12 +434,35 @@ var ModelGroup = class extends Group {
|
|
|
376
434
|
this.people.set(name, person);
|
|
377
435
|
return person;
|
|
378
436
|
}
|
|
437
|
+
group(groupName) {
|
|
438
|
+
let group = this._groups.get(groupName);
|
|
439
|
+
if (!group) {
|
|
440
|
+
group = new _ModelGroup(groupName, this.model, [...this.pathSegments, this.name]);
|
|
441
|
+
this._groups.set(groupName, group);
|
|
442
|
+
}
|
|
443
|
+
return group;
|
|
444
|
+
}
|
|
445
|
+
getGroups() {
|
|
446
|
+
return Array.from(this._groups.values());
|
|
447
|
+
}
|
|
379
448
|
getSoftwareSystems() {
|
|
380
449
|
return Array.from(this.softwareSystems.values());
|
|
381
450
|
}
|
|
382
451
|
getPeople() {
|
|
383
452
|
return Array.from(this.people.values());
|
|
384
453
|
}
|
|
454
|
+
getAllSoftwareSystems() {
|
|
455
|
+
return [
|
|
456
|
+
...this.softwareSystems.values(),
|
|
457
|
+
...Array.from(this._groups.values()).flatMap((g) => g.getAllSoftwareSystems())
|
|
458
|
+
];
|
|
459
|
+
}
|
|
460
|
+
getAllPeople() {
|
|
461
|
+
return [
|
|
462
|
+
...this.people.values(),
|
|
463
|
+
...Array.from(this._groups.values()).flatMap((g) => g.getAllPeople())
|
|
464
|
+
];
|
|
465
|
+
}
|
|
385
466
|
};
|
|
386
467
|
var Model = class {
|
|
387
468
|
constructor(name) {
|
|
@@ -406,7 +487,6 @@ var Model = class {
|
|
|
406
487
|
this.softwareSystems.set(name, system);
|
|
407
488
|
return system;
|
|
408
489
|
}
|
|
409
|
-
// TODO:Should be a Group<SoftwareSystem | Person> if that is added back in
|
|
410
490
|
group(groupName) {
|
|
411
491
|
let group = this.groups.get(groupName);
|
|
412
492
|
if (!group) {
|
|
@@ -441,11 +521,11 @@ var Model = class {
|
|
|
441
521
|
return Array.from(this.softwareSystems.values());
|
|
442
522
|
}
|
|
443
523
|
getPeopleNotInGroups() {
|
|
444
|
-
const peopleInGroups = Array.from(this.groups.values()).flatMap((group) => group.
|
|
524
|
+
const peopleInGroups = Array.from(this.groups.values()).flatMap((group) => group.getAllPeople());
|
|
445
525
|
return Array.from(this.people.values()).filter((person) => !peopleInGroups.includes(person));
|
|
446
526
|
}
|
|
447
527
|
getSoftwareSystemsNotInGroups() {
|
|
448
|
-
const systemsInGroups = Array.from(this.groups.values()).flatMap((group) => group.
|
|
528
|
+
const systemsInGroups = Array.from(this.groups.values()).flatMap((group) => group.getAllSoftwareSystems());
|
|
449
529
|
return Array.from(this.softwareSystems.values()).filter((system) => !systemsInGroups.includes(system));
|
|
450
530
|
}
|
|
451
531
|
getGroups() {
|
|
@@ -465,6 +545,9 @@ var View = class {
|
|
|
465
545
|
description;
|
|
466
546
|
title;
|
|
467
547
|
_scopes = [];
|
|
548
|
+
_autoLayout;
|
|
549
|
+
_isDefault = false;
|
|
550
|
+
_properties = /* @__PURE__ */ new Map();
|
|
468
551
|
includeAll() {
|
|
469
552
|
this._scopes.push("include *");
|
|
470
553
|
}
|
|
@@ -483,15 +566,37 @@ var View = class {
|
|
|
483
566
|
excludeExpression(expression) {
|
|
484
567
|
this._scopes.push(`exclude ${expression}`);
|
|
485
568
|
}
|
|
569
|
+
autoLayout(direction, rankSeparation, nodeSeparation) {
|
|
570
|
+
this._autoLayout = { direction, rankSeparation, nodeSeparation };
|
|
571
|
+
}
|
|
572
|
+
setDefault() {
|
|
573
|
+
this._isDefault = true;
|
|
574
|
+
}
|
|
575
|
+
addProperty(name, value) {
|
|
576
|
+
this._properties.set(name, value);
|
|
577
|
+
}
|
|
486
578
|
get scopes() {
|
|
487
579
|
return this._scopes;
|
|
488
580
|
}
|
|
581
|
+
get autoLayoutConfig() {
|
|
582
|
+
return this._autoLayout;
|
|
583
|
+
}
|
|
584
|
+
get isDefault() {
|
|
585
|
+
return this._isDefault;
|
|
586
|
+
}
|
|
587
|
+
get properties() {
|
|
588
|
+
return this._properties;
|
|
589
|
+
}
|
|
489
590
|
};
|
|
490
591
|
var Views = class {
|
|
491
592
|
_systemLandscapeViews = /* @__PURE__ */ new Map();
|
|
492
593
|
_systemContextViews = /* @__PURE__ */ new Map();
|
|
493
594
|
_containerViews = /* @__PURE__ */ new Map();
|
|
494
595
|
_componentViews = /* @__PURE__ */ new Map();
|
|
596
|
+
_elementStyles = [];
|
|
597
|
+
_relationshipStyles = [];
|
|
598
|
+
_themes = [];
|
|
599
|
+
_properties = /* @__PURE__ */ new Map();
|
|
495
600
|
addSystemLandscapeView(key, definition) {
|
|
496
601
|
const view = new View(key, { subject: void 0, description: definition.description, title: definition.title });
|
|
497
602
|
this._systemLandscapeViews.set(key, view);
|
|
@@ -512,6 +617,18 @@ var Views = class {
|
|
|
512
617
|
this._componentViews.set(key, view);
|
|
513
618
|
return view;
|
|
514
619
|
}
|
|
620
|
+
addElementStyle(tag, definition) {
|
|
621
|
+
this._elementStyles.push({ tag, definition });
|
|
622
|
+
}
|
|
623
|
+
addRelationshipStyle(tag, definition) {
|
|
624
|
+
this._relationshipStyles.push({ tag, definition });
|
|
625
|
+
}
|
|
626
|
+
addTheme(url) {
|
|
627
|
+
this._themes.push(url);
|
|
628
|
+
}
|
|
629
|
+
addProperty(name, value) {
|
|
630
|
+
this._properties.set(name, value);
|
|
631
|
+
}
|
|
515
632
|
get systemLandscapeViews() {
|
|
516
633
|
return Array.from(this._systemLandscapeViews.values());
|
|
517
634
|
}
|
|
@@ -524,6 +641,18 @@ var Views = class {
|
|
|
524
641
|
get componentViews() {
|
|
525
642
|
return Array.from(this._componentViews.values());
|
|
526
643
|
}
|
|
644
|
+
get elementStyles() {
|
|
645
|
+
return this._elementStyles;
|
|
646
|
+
}
|
|
647
|
+
get relationshipStyles() {
|
|
648
|
+
return this._relationshipStyles;
|
|
649
|
+
}
|
|
650
|
+
get themes() {
|
|
651
|
+
return this._themes;
|
|
652
|
+
}
|
|
653
|
+
get properties() {
|
|
654
|
+
return this._properties;
|
|
655
|
+
}
|
|
527
656
|
};
|
|
528
657
|
|
|
529
658
|
// libs/c4-model/src/structurizrDslWriter.ts
|
|
@@ -658,11 +787,17 @@ var StructurizrDSLWriter = class {
|
|
|
658
787
|
}
|
|
659
788
|
writeContainerGroup(group, level) {
|
|
660
789
|
let containerGroupDsl = "";
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
containerGroupDsl += this.
|
|
790
|
+
const hasDirect = group.getComponents().length > 0;
|
|
791
|
+
if (hasDirect) {
|
|
792
|
+
containerGroupDsl += this.writeLine(`${group.canonicalName} = group "${group.dslName}" {`, level);
|
|
793
|
+
group.getComponents().forEach((component) => {
|
|
794
|
+
containerGroupDsl += this.writeComponent(component, level + 1);
|
|
795
|
+
});
|
|
796
|
+
containerGroupDsl += this.writeLine(`}`, level);
|
|
797
|
+
}
|
|
798
|
+
group.getGroups().forEach((nested) => {
|
|
799
|
+
containerGroupDsl += this.writeContainerGroup(nested, level);
|
|
664
800
|
});
|
|
665
|
-
containerGroupDsl += this.writeLine(`}`, level);
|
|
666
801
|
return containerGroupDsl;
|
|
667
802
|
}
|
|
668
803
|
writeContainer(container, level) {
|
|
@@ -682,11 +817,17 @@ var StructurizrDSLWriter = class {
|
|
|
682
817
|
}
|
|
683
818
|
writeSoftwareSystemGroup(group, level) {
|
|
684
819
|
let softwareSystemGroupDsl = "";
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
softwareSystemGroupDsl += this.
|
|
820
|
+
const hasDirect = group.getContainers().length > 0;
|
|
821
|
+
if (hasDirect) {
|
|
822
|
+
softwareSystemGroupDsl += this.writeLine(`${group.canonicalName} = group "${group.dslName}" {`, level);
|
|
823
|
+
group.getContainers().forEach((container) => {
|
|
824
|
+
softwareSystemGroupDsl += this.writeContainer(container, level + 1);
|
|
825
|
+
});
|
|
826
|
+
softwareSystemGroupDsl += this.writeLine(`}`, level);
|
|
827
|
+
}
|
|
828
|
+
group.getGroups().forEach((nested) => {
|
|
829
|
+
softwareSystemGroupDsl += this.writeSoftwareSystemGroup(nested, level);
|
|
688
830
|
});
|
|
689
|
-
softwareSystemGroupDsl += this.writeLine(`}`, level);
|
|
690
831
|
return softwareSystemGroupDsl;
|
|
691
832
|
}
|
|
692
833
|
writeSoftwareSystem(softwareSystem, level) {
|
|
@@ -738,21 +879,51 @@ var StructurizrDSLWriter = class {
|
|
|
738
879
|
});
|
|
739
880
|
return relationshipsDsl;
|
|
740
881
|
}
|
|
882
|
+
hasNestedModelGroups(groups) {
|
|
883
|
+
return groups.some((g) => g.getGroups().length > 0 || this.hasNestedModelGroups(g.getGroups()));
|
|
884
|
+
}
|
|
885
|
+
hasNestedSoftwareSystemGroups(groups) {
|
|
886
|
+
return groups.some((g) => g.getGroups().length > 0 || this.hasNestedSoftwareSystemGroups(g.getGroups()));
|
|
887
|
+
}
|
|
888
|
+
hasNestedContainerGroups(groups) {
|
|
889
|
+
return groups.some((g) => g.getGroups().length > 0 || this.hasNestedContainerGroups(g.getGroups()));
|
|
890
|
+
}
|
|
891
|
+
hasNestedGroups() {
|
|
892
|
+
if (this.hasNestedModelGroups(this.model.getGroups())) return true;
|
|
893
|
+
for (const ss of this.model.getSoftwareSystems()) {
|
|
894
|
+
if (this.hasNestedSoftwareSystemGroups(ss.getGroups())) return true;
|
|
895
|
+
for (const c of ss.getGroups().flatMap((g) => g.getAllContainers())) {
|
|
896
|
+
if (this.hasNestedContainerGroups(c.getGroups())) return true;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
return false;
|
|
900
|
+
}
|
|
741
901
|
writeModelGroup(group, level) {
|
|
742
902
|
let modelGroupDsl = "";
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
modelGroupDsl += this.
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
903
|
+
const hasDirect = group.getPeople().length > 0 || group.getSoftwareSystems().length > 0;
|
|
904
|
+
if (hasDirect) {
|
|
905
|
+
modelGroupDsl += this.writeLine(`${group.canonicalName} = group "${group.dslName}" {`, level);
|
|
906
|
+
group.getPeople().forEach((person) => {
|
|
907
|
+
modelGroupDsl += this.writeElement("person", person, level + 1);
|
|
908
|
+
});
|
|
909
|
+
group.getSoftwareSystems().forEach((softwareSystem) => {
|
|
910
|
+
modelGroupDsl += this.writeSoftwareSystem(softwareSystem, level + 1);
|
|
911
|
+
});
|
|
912
|
+
modelGroupDsl += this.writeLine(`}`, level);
|
|
913
|
+
}
|
|
914
|
+
group.getGroups().forEach((nested) => {
|
|
915
|
+
modelGroupDsl += this.writeModelGroup(nested, level);
|
|
749
916
|
});
|
|
750
|
-
modelGroupDsl += this.writeLine(`}`, level);
|
|
751
917
|
return modelGroupDsl;
|
|
752
918
|
}
|
|
753
919
|
writeModel(model, level) {
|
|
754
920
|
let modelDsl = "";
|
|
755
921
|
modelDsl += this.writeLine(`model {`, level);
|
|
922
|
+
if (this.hasNestedGroups()) {
|
|
923
|
+
modelDsl += this.writeLine(`properties {`, level + 1);
|
|
924
|
+
modelDsl += this.writeLine(`"structurizr.groupSeparator" "/"`, level + 2);
|
|
925
|
+
modelDsl += this.writeLine(`}`, level + 1);
|
|
926
|
+
}
|
|
756
927
|
modelDsl += this.writeArchetypes(level + 1);
|
|
757
928
|
modelDsl += this.writeLine("// Elements", level + 1);
|
|
758
929
|
model.getPeopleNotInGroups().forEach((person) => {
|
|
@@ -775,12 +946,66 @@ var StructurizrDSLWriter = class {
|
|
|
775
946
|
if (view.title) {
|
|
776
947
|
viewDsl += this.writeLine(`title "${view.title}"`, level + 1);
|
|
777
948
|
}
|
|
949
|
+
if (view.isDefault) {
|
|
950
|
+
viewDsl += this.writeLine("default", level + 1);
|
|
951
|
+
}
|
|
778
952
|
view.scopes.forEach((scope) => {
|
|
779
953
|
viewDsl += this.writeLine(`${scope}`, level + 1);
|
|
780
954
|
});
|
|
955
|
+
if (view.autoLayoutConfig) {
|
|
956
|
+
const { direction, rankSeparation, nodeSeparation } = view.autoLayoutConfig;
|
|
957
|
+
let line = "autoLayout";
|
|
958
|
+
if (direction) line += ` ${direction}`;
|
|
959
|
+
if (rankSeparation !== void 0) line += ` ${rankSeparation}`;
|
|
960
|
+
if (nodeSeparation !== void 0) line += ` ${nodeSeparation}`;
|
|
961
|
+
viewDsl += this.writeLine(line, level + 1);
|
|
962
|
+
}
|
|
963
|
+
if (view.properties.size > 0) {
|
|
964
|
+
viewDsl += this.writeLine("properties {", level + 1);
|
|
965
|
+
for (const [name, value] of view.properties) {
|
|
966
|
+
viewDsl += this.writeLine(`"${name}" "${value}"`, level + 2);
|
|
967
|
+
}
|
|
968
|
+
viewDsl += this.writeLine("}", level + 1);
|
|
969
|
+
}
|
|
781
970
|
viewDsl += this.writeLine(`}`, level);
|
|
782
971
|
return viewDsl;
|
|
783
972
|
}
|
|
973
|
+
writeStyles(views, level) {
|
|
974
|
+
const { elementStyles, relationshipStyles } = views;
|
|
975
|
+
if (elementStyles.length === 0 && relationshipStyles.length === 0) return "";
|
|
976
|
+
let dsl = this.writeLine("styles {", level);
|
|
977
|
+
for (const { tag, definition: d } of elementStyles) {
|
|
978
|
+
dsl += this.writeLine(`element "${tag}" {`, level + 1);
|
|
979
|
+
if (d.shape) dsl += this.writeLine(`shape ${d.shape}`, level + 2);
|
|
980
|
+
if (d.icon) dsl += this.writeLine(`icon "${d.icon}"`, level + 2);
|
|
981
|
+
if (d.width) dsl += this.writeLine(`width ${d.width}`, level + 2);
|
|
982
|
+
if (d.height) dsl += this.writeLine(`height ${d.height}`, level + 2);
|
|
983
|
+
if (d.background) dsl += this.writeLine(`background "${d.background}"`, level + 2);
|
|
984
|
+
if (d.color) dsl += this.writeLine(`color "${d.color}"`, level + 2);
|
|
985
|
+
if (d.stroke) dsl += this.writeLine(`stroke "${d.stroke}"`, level + 2);
|
|
986
|
+
if (d.strokeWidth) dsl += this.writeLine(`strokeWidth ${d.strokeWidth}`, level + 2);
|
|
987
|
+
if (d.fontSize) dsl += this.writeLine(`fontSize ${d.fontSize}`, level + 2);
|
|
988
|
+
if (d.border) dsl += this.writeLine(`border ${d.border}`, level + 2);
|
|
989
|
+
if (d.opacity !== void 0) dsl += this.writeLine(`opacity ${d.opacity}`, level + 2);
|
|
990
|
+
if (d.metadata !== void 0) dsl += this.writeLine(`metadata ${d.metadata}`, level + 2);
|
|
991
|
+
if (d.description !== void 0) dsl += this.writeLine(`description ${d.description}`, level + 2);
|
|
992
|
+
dsl += this.writeLine("}", level + 1);
|
|
993
|
+
}
|
|
994
|
+
for (const { tag, definition: d } of relationshipStyles) {
|
|
995
|
+
dsl += this.writeLine(`relationship "${tag}" {`, level + 1);
|
|
996
|
+
if (d.thickness) dsl += this.writeLine(`thickness ${d.thickness}`, level + 2);
|
|
997
|
+
if (d.color) dsl += this.writeLine(`color "${d.color}"`, level + 2);
|
|
998
|
+
if (d.style) dsl += this.writeLine(`style ${d.style}`, level + 2);
|
|
999
|
+
if (d.routing) dsl += this.writeLine(`routing ${d.routing}`, level + 2);
|
|
1000
|
+
if (d.fontSize) dsl += this.writeLine(`fontSize ${d.fontSize}`, level + 2);
|
|
1001
|
+
if (d.width) dsl += this.writeLine(`width ${d.width}`, level + 2);
|
|
1002
|
+
if (d.position !== void 0) dsl += this.writeLine(`position ${d.position}`, level + 2);
|
|
1003
|
+
if (d.opacity !== void 0) dsl += this.writeLine(`opacity ${d.opacity}`, level + 2);
|
|
1004
|
+
dsl += this.writeLine("}", level + 1);
|
|
1005
|
+
}
|
|
1006
|
+
dsl += this.writeLine("}", level);
|
|
1007
|
+
return dsl;
|
|
1008
|
+
}
|
|
784
1009
|
writeViews(views, level) {
|
|
785
1010
|
let viewDsl = "";
|
|
786
1011
|
viewDsl += this.writeLine(`views {`, level);
|
|
@@ -800,6 +1025,19 @@ var StructurizrDSLWriter = class {
|
|
|
800
1025
|
views.componentViews.forEach((view) => {
|
|
801
1026
|
viewDsl += this.writeView(view, "component", level + 1);
|
|
802
1027
|
});
|
|
1028
|
+
viewDsl += this.writeStyles(views, level + 1);
|
|
1029
|
+
if (views.themes.length === 1) {
|
|
1030
|
+
viewDsl += this.writeLine(`theme ${views.themes[0]}`, level + 1);
|
|
1031
|
+
} else if (views.themes.length > 1) {
|
|
1032
|
+
viewDsl += this.writeLine(`themes ${views.themes.join(" ")}`, level + 1);
|
|
1033
|
+
}
|
|
1034
|
+
if (views.properties.size > 0) {
|
|
1035
|
+
viewDsl += this.writeLine("properties {", level + 1);
|
|
1036
|
+
for (const [name, value] of views.properties) {
|
|
1037
|
+
viewDsl += this.writeLine(`"${name}" "${value}"`, level + 2);
|
|
1038
|
+
}
|
|
1039
|
+
viewDsl += this.writeLine("}", level + 1);
|
|
1040
|
+
}
|
|
803
1041
|
viewDsl += this.writeLine(`}`, level);
|
|
804
1042
|
return viewDsl;
|
|
805
1043
|
}
|
|
@@ -824,8 +1062,8 @@ import { glob } from "glob";
|
|
|
824
1062
|
import { join, dirname } from "path";
|
|
825
1063
|
import { fileURLToPath } from "url";
|
|
826
1064
|
var _dirname = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLToPath(import.meta.url));
|
|
827
|
-
async function
|
|
828
|
-
const { modelName = "model", modules: explicitModules, archetypes = {} } = options;
|
|
1065
|
+
async function buildModel(options = {}) {
|
|
1066
|
+
const { modelName = "model", modules: explicitModules, archetypes = {}, addViews } = options;
|
|
829
1067
|
const model = new Model(modelName);
|
|
830
1068
|
let c4Modules;
|
|
831
1069
|
if (explicitModules) {
|
|
@@ -849,14 +1087,16 @@ async function buildModelWithCatalog(options = {}) {
|
|
|
849
1087
|
rootCatalog[instance.key] = local;
|
|
850
1088
|
registrations.push({ instance, key: instance.key, local });
|
|
851
1089
|
}
|
|
1090
|
+
const dependenciesFor = (key) => Object.fromEntries(Object.entries(rootCatalog).filter(([k]) => k !== key));
|
|
852
1091
|
for (const { instance, key, local } of registrations) {
|
|
853
|
-
|
|
854
|
-
instance.buildRelationships(local, dependencies, archetypes);
|
|
1092
|
+
instance.addRelationships(local, dependenciesFor(key), archetypes);
|
|
855
1093
|
}
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
1094
|
+
const views = new Views();
|
|
1095
|
+
addViews?.(views, rootCatalog);
|
|
1096
|
+
for (const { instance, key, local } of registrations) {
|
|
1097
|
+
instance.addViews?.(views, local, dependenciesFor(key));
|
|
1098
|
+
}
|
|
1099
|
+
return { model, catalog: rootCatalog, views };
|
|
860
1100
|
}
|
|
861
1101
|
|
|
862
1102
|
// libs/c4-model/src/generateDiagrams.ts
|
|
@@ -865,9 +1105,8 @@ import * as os from "os";
|
|
|
865
1105
|
import * as path from "path";
|
|
866
1106
|
import { GenericContainer, Wait } from "testcontainers";
|
|
867
1107
|
async function generateDiagrams(options) {
|
|
868
|
-
const {
|
|
869
|
-
const { model,
|
|
870
|
-
const views = viewsFactory(catalog);
|
|
1108
|
+
const { outputDir, ...buildOptions } = options;
|
|
1109
|
+
const { model, views } = await buildModel(buildOptions);
|
|
871
1110
|
const dsl = new StructurizrDSLWriter(model, views).write();
|
|
872
1111
|
const tmpDir = await fs.promises.mkdtemp(path.join(fs.realpathSync(os.tmpdir()), "c4-diagrams-"));
|
|
873
1112
|
await fs.promises.writeFile(path.join(tmpDir, "workspace.dsl"), dsl, "utf8");
|
|
@@ -888,6 +1127,60 @@ async function generateDiagrams(options) {
|
|
|
888
1127
|
}
|
|
889
1128
|
return generatedFiles;
|
|
890
1129
|
}
|
|
1130
|
+
|
|
1131
|
+
// libs/c4-model/src/validateModel.ts
|
|
1132
|
+
import * as fs2 from "fs";
|
|
1133
|
+
import * as os2 from "os";
|
|
1134
|
+
import * as path2 from "path";
|
|
1135
|
+
import { GenericContainer as GenericContainer2, Wait as Wait2 } from "testcontainers";
|
|
1136
|
+
async function validateModel(model, views) {
|
|
1137
|
+
const dsl = new StructurizrDSLWriter(model, views).write();
|
|
1138
|
+
const tmpDir = await fs2.promises.mkdtemp(path2.join(fs2.realpathSync(os2.tmpdir()), "c4-validate-"));
|
|
1139
|
+
try {
|
|
1140
|
+
await fs2.promises.writeFile(path2.join(tmpDir, "workspace.dsl"), dsl, "utf8");
|
|
1141
|
+
const logs = [];
|
|
1142
|
+
try {
|
|
1143
|
+
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();
|
|
1144
|
+
} catch {
|
|
1145
|
+
throw new Error(`Structurizr validation failed:
|
|
1146
|
+
${logs.join("")}`);
|
|
1147
|
+
}
|
|
1148
|
+
} finally {
|
|
1149
|
+
await fs2.promises.rm(tmpDir, { recursive: true, force: true });
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
// libs/c4-model/src/exportWorkspaceJson.ts
|
|
1154
|
+
import * as fs3 from "fs";
|
|
1155
|
+
import * as os3 from "os";
|
|
1156
|
+
import * as path3 from "path";
|
|
1157
|
+
import { GenericContainer as GenericContainer3, Wait as Wait3 } from "testcontainers";
|
|
1158
|
+
async function exportWorkspaceJsonFromDsl(dsl) {
|
|
1159
|
+
const tmpDir = await fs3.promises.mkdtemp(path3.join(fs3.realpathSync(os3.tmpdir()), "c4-export-"));
|
|
1160
|
+
try {
|
|
1161
|
+
await fs3.promises.writeFile(path3.join(tmpDir, "workspace.dsl"), dsl, "utf8");
|
|
1162
|
+
const logs = [];
|
|
1163
|
+
try {
|
|
1164
|
+
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();
|
|
1165
|
+
} catch {
|
|
1166
|
+
throw new Error(`Structurizr JSON export failed:
|
|
1167
|
+
${logs.join("")}`);
|
|
1168
|
+
}
|
|
1169
|
+
const files = await fs3.promises.readdir(tmpDir);
|
|
1170
|
+
const jsonFile = files.find((f) => f.endsWith(".json"));
|
|
1171
|
+
if (!jsonFile) {
|
|
1172
|
+
throw new Error(`Structurizr JSON export produced no .json file. Files: ${files.join(", ")}`);
|
|
1173
|
+
}
|
|
1174
|
+
const jsonContent = await fs3.promises.readFile(path3.join(tmpDir, jsonFile), "utf8");
|
|
1175
|
+
return JSON.parse(jsonContent);
|
|
1176
|
+
} finally {
|
|
1177
|
+
await fs3.promises.rm(tmpDir, { recursive: true, force: true });
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
async function exportWorkspaceJson(model, views) {
|
|
1181
|
+
const dsl = new StructurizrDSLWriter(model, views).write();
|
|
1182
|
+
return exportWorkspaceJsonFromDsl(dsl);
|
|
1183
|
+
}
|
|
891
1184
|
export {
|
|
892
1185
|
Component,
|
|
893
1186
|
Container,
|
|
@@ -903,7 +1196,9 @@ export {
|
|
|
903
1196
|
View,
|
|
904
1197
|
Views,
|
|
905
1198
|
buildModel,
|
|
906
|
-
|
|
1199
|
+
exportWorkspaceJson,
|
|
1200
|
+
exportWorkspaceJsonFromDsl,
|
|
907
1201
|
generateDiagrams,
|
|
908
|
-
mergeArchetypeWithOverride
|
|
1202
|
+
mergeArchetypeWithOverride,
|
|
1203
|
+
validateModel
|
|
909
1204
|
};
|
package/src/model.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Group } from './core';
|
|
|
2
2
|
import { SoftwareSystem, SoftwareSystemDefinition } from './softwareSystem';
|
|
3
3
|
import { Person, PersonDefinition } from './person';
|
|
4
4
|
import { ElementArchetype, RelationshipArchetype } from './archetype';
|
|
5
|
+
import { Views } from './views';
|
|
5
6
|
export type CatalogKeyOf<TRoot, TModule> = {
|
|
6
7
|
[K in keyof TRoot]: TRoot[K] extends TModule ? K : never;
|
|
7
8
|
}[keyof TRoot];
|
|
@@ -9,7 +10,8 @@ export type Dependencies<TRoot, TModule> = Omit<TRoot, CatalogKeyOf<TRoot, TModu
|
|
|
9
10
|
export interface C4Module<TRoot, TLocal, TArchetypes = Record<string, ElementArchetype | RelationshipArchetype>> {
|
|
10
11
|
readonly key: CatalogKeyOf<TRoot, TLocal>;
|
|
11
12
|
registerDefinitions(model: Model, archetypes: TArchetypes): TLocal;
|
|
12
|
-
|
|
13
|
+
addRelationships(local: TLocal, dependencies: Dependencies<TRoot, TLocal>, archetypes: TArchetypes): void;
|
|
14
|
+
addViews?(views: Views, local: TLocal, dependencies: Dependencies<TRoot, TLocal>): void;
|
|
13
15
|
}
|
|
14
16
|
interface DefineSoftwareSystem {
|
|
15
17
|
softwareSystem(name: string, archetypeOrDef?: ElementArchetype | SoftwareSystemDefinition, override?: SoftwareSystemDefinition): SoftwareSystem;
|
|
@@ -20,13 +22,21 @@ interface DefinePerson {
|
|
|
20
22
|
export declare class ModelGroup extends Group<Person | SoftwareSystem> implements DefineSoftwareSystem, DefinePerson {
|
|
21
23
|
readonly name: string;
|
|
22
24
|
private readonly model;
|
|
25
|
+
private readonly pathSegments;
|
|
23
26
|
private softwareSystems;
|
|
24
27
|
private people;
|
|
25
|
-
|
|
28
|
+
private _groups;
|
|
29
|
+
constructor(name: string, model: DefineSoftwareSystem & DefinePerson, pathSegments?: string[]);
|
|
30
|
+
get canonicalName(): string;
|
|
31
|
+
get dslName(): string;
|
|
26
32
|
softwareSystem(name: string, archetypeOrDef?: ElementArchetype | SoftwareSystemDefinition, override?: SoftwareSystemDefinition): SoftwareSystem;
|
|
27
33
|
person(name: string, archetypeOrDef?: ElementArchetype | PersonDefinition, override?: PersonDefinition): Person;
|
|
34
|
+
group(groupName: string): ModelGroup;
|
|
35
|
+
getGroups(): ReadonlyArray<ModelGroup>;
|
|
28
36
|
getSoftwareSystems(): ReadonlyArray<SoftwareSystem>;
|
|
29
37
|
getPeople(): ReadonlyArray<Person>;
|
|
38
|
+
getAllSoftwareSystems(): ReadonlyArray<SoftwareSystem>;
|
|
39
|
+
getAllPeople(): ReadonlyArray<Person>;
|
|
30
40
|
}
|
|
31
41
|
export interface ModelDefinitions {
|
|
32
42
|
softwareSystem(name: string, archetypeOrDef?: ElementArchetype | SoftwareSystemDefinition, override?: SoftwareSystemDefinition): SoftwareSystem;
|
|
@@ -39,7 +49,7 @@ export declare class Model {
|
|
|
39
49
|
private people;
|
|
40
50
|
private groups;
|
|
41
51
|
softwareSystem(name: string, archetypeOrDef?: ElementArchetype | SoftwareSystemDefinition, override?: SoftwareSystemDefinition): SoftwareSystem;
|
|
42
|
-
group(groupName: string):
|
|
52
|
+
group(groupName: string): ModelGroup;
|
|
43
53
|
person(name: string, archetypeOrDef?: ElementArchetype | PersonDefinition, override?: PersonDefinition): Person;
|
|
44
54
|
validate(): void;
|
|
45
55
|
getPeople(): ReadonlyArray<Person>;
|
package/src/softwareSystem.d.ts
CHANGED
|
@@ -11,10 +11,17 @@ export interface SoftwareSystemReference {
|
|
|
11
11
|
export declare class SoftwareSystemGroup extends Group<Container> implements DefineContainer {
|
|
12
12
|
readonly name: string;
|
|
13
13
|
private readonly softwareSystem;
|
|
14
|
+
private readonly pathSegments;
|
|
14
15
|
private _containers;
|
|
15
|
-
|
|
16
|
+
private _groups;
|
|
17
|
+
constructor(name: string, softwareSystem: DefineContainer, pathSegments?: string[]);
|
|
18
|
+
get canonicalName(): string;
|
|
19
|
+
get dslName(): string;
|
|
16
20
|
container(name: string, archetypeOrDef?: ElementArchetype | ContainerDefinition, override?: ContainerDefinition): Container;
|
|
21
|
+
group(groupName: string): SoftwareSystemGroup;
|
|
22
|
+
getGroups(): ReadonlyArray<SoftwareSystemGroup>;
|
|
17
23
|
getContainers(): ReadonlyArray<Container>;
|
|
24
|
+
getAllContainers(): ReadonlyArray<Container>;
|
|
18
25
|
}
|
|
19
26
|
export declare class SoftwareSystem extends Element<Container> implements DefineContainer {
|
|
20
27
|
readonly name: string;
|
|
@@ -14,9 +14,14 @@ export declare class StructurizrDSLWriter {
|
|
|
14
14
|
private writeSoftwareSystem;
|
|
15
15
|
private writeRelationship;
|
|
16
16
|
private writeRelationships;
|
|
17
|
+
private hasNestedModelGroups;
|
|
18
|
+
private hasNestedSoftwareSystemGroups;
|
|
19
|
+
private hasNestedContainerGroups;
|
|
20
|
+
private hasNestedGroups;
|
|
17
21
|
private writeModelGroup;
|
|
18
22
|
private writeModel;
|
|
19
23
|
private writeView;
|
|
24
|
+
private writeStyles;
|
|
20
25
|
private writeViews;
|
|
21
26
|
write(): string;
|
|
22
27
|
private writeLine;
|