@likec4/core 1.46.4 → 1.48.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/LICENSE +1 -1
- package/dist/_chunks/Builder.view-element.mjs +3284 -0
- package/dist/_chunks/LikeC4Model.d.mts +1287 -0
- package/dist/_chunks/LikeC4Styles.mjs +691 -0
- package/dist/_chunks/RichText.d.mts +84 -0
- package/dist/_chunks/RichText.mjs +148 -0
- package/dist/_chunks/_aux.d.mts +289 -0
- package/dist/_chunks/_aux.mjs +18 -0
- package/dist/_chunks/_types.d.mts +781 -0
- package/dist/_chunks/_types2.d.mts +38 -0
- package/dist/_chunks/assignTagColors.mjs +24 -0
- package/dist/_chunks/calcDriftsFromSnapshot.mjs +379 -0
- package/dist/_chunks/connection.mjs +0 -0
- package/dist/_chunks/const.d.mts +43 -0
- package/dist/_chunks/const.mjs +33 -0
- package/dist/_chunks/expression-model.d.mts +192 -0
- package/dist/_chunks/expression-model.mjs +103 -0
- package/dist/_chunks/expression.d.mts +347 -0
- package/dist/_chunks/expression.mjs +107 -0
- package/dist/_chunks/find.d.mts +82 -0
- package/dist/_chunks/find.mjs +646 -0
- package/dist/_chunks/find2.d.mts +78 -0
- package/dist/_chunks/find2.mjs +287 -0
- package/dist/_chunks/fqnRef.d.mts +58 -0
- package/dist/_chunks/fqnRef.mjs +41 -0
- package/dist/_chunks/geometry.mjs +335 -0
- package/dist/_chunks/index.d.mts +169 -0
- package/dist/_chunks/index2.d.mts +937 -0
- package/dist/_chunks/index3.d.mts +146 -0
- package/dist/_chunks/index4.d.mts +62 -0
- package/dist/_chunks/index5.d.mts +42 -0
- package/dist/_chunks/index6.d.mts +43 -0
- package/dist/{shared/core.OtRNdMxy.d.mts → _chunks/index7.d.mts} +113 -197
- package/dist/_chunks/index8.d.mts +144 -0
- package/dist/_chunks/invariant.mjs +18 -0
- package/dist/_chunks/iterable.mjs +146 -0
- package/dist/_chunks/libs/@mantine/colors-generator.mjs +3416 -0
- package/dist/_chunks/libs/@ungap/structured-clone.mjs +213 -0
- package/dist/_chunks/libs/bail.mjs +14 -0
- package/dist/_chunks/libs/ccount.mjs +25 -0
- package/dist/_chunks/libs/character-entities-html4.mjs +263 -0
- package/dist/_chunks/libs/character-entities-legacy.mjs +117 -0
- package/dist/_chunks/libs/character-entities.mjs +2136 -0
- package/dist/_chunks/libs/comma-separated-tokens.mjs +18 -0
- package/dist/_chunks/libs/decode-named-character-reference.mjs +21 -0
- package/dist/_chunks/libs/defu.mjs +42 -0
- package/dist/_chunks/libs/devlop.mjs +5 -0
- package/dist/_chunks/libs/escape-string-regexp.mjs +8 -0
- package/dist/_chunks/libs/extend.mjs +77 -0
- package/dist/_chunks/libs/graphology-dag.mjs +703 -0
- package/dist/_chunks/libs/graphology-types.d.mts +601 -0
- package/dist/_chunks/libs/graphology.d.mts +6 -0
- package/dist/_chunks/libs/graphology.mjs +4565 -0
- package/dist/_chunks/libs/hast-util-sanitize.mjs +863 -0
- package/dist/_chunks/libs/hast-util-to-html.mjs +2696 -0
- package/dist/_chunks/libs/is-plain-obj.mjs +9 -0
- package/dist/_chunks/libs/longest-streak.mjs +30 -0
- package/dist/_chunks/libs/markdown-table.mjs +290 -0
- package/dist/_chunks/libs/mdast-util-find-and-replace.mjs +653 -0
- package/dist/_chunks/libs/mdast-util-from-markdown.mjs +8522 -0
- package/dist/_chunks/libs/mdast-util-gfm.mjs +2393 -0
- package/dist/_chunks/libs/mdast-util-to-hast.mjs +1803 -0
- package/dist/_chunks/libs/micromark-extension-gfm.mjs +2243 -0
- package/dist/_chunks/libs/mnemonist.d.mts +254 -0
- package/dist/_chunks/libs/mnemonist.mjs +1978 -0
- package/dist/_chunks/libs/natural-compare-lite.mjs +42 -0
- package/dist/_chunks/libs/rehype-sanitize.mjs +29 -0
- package/dist/_chunks/libs/rehype-stringify.mjs +34 -0
- package/dist/_chunks/libs/remark-gfm.mjs +36 -0
- package/dist/_chunks/libs/remark-github-blockquote-alert.mjs +100 -0
- package/dist/_chunks/libs/remark-parse.mjs +39 -0
- package/dist/_chunks/libs/remark-rehype.mjs +171 -0
- package/dist/_chunks/libs/remeda.mjs +702 -0
- package/dist/_chunks/libs/trough.mjs +158 -0
- package/dist/_chunks/libs/unified.mjs +2212 -0
- package/dist/_chunks/mnemonist.d.mts +1 -0
- package/dist/_chunks/mnemonist.mjs +0 -0
- package/dist/_chunks/model.mjs +1898 -0
- package/dist/_chunks/model2.mjs +0 -0
- package/dist/_chunks/relationships-view.mjs +132 -0
- package/dist/_chunks/rolldown-runtime.mjs +42 -0
- package/dist/{shared/core.C_3y3eqV.d.mts → _chunks/scalar.d.mts} +13 -43
- package/dist/_chunks/scalar.mjs +84 -0
- package/dist/_chunks/utils.mjs +483 -0
- package/dist/builder/index.d.mts +11 -773
- package/dist/builder/index.mjs +631 -978
- package/dist/compute-view/index.d.mts +90 -27
- package/dist/compute-view/index.mjs +253 -83
- package/dist/compute-view/relationships-view/index.d.mts +16 -51
- package/dist/compute-view/relationships-view/index.mjs +6 -184
- package/dist/geometry/index.d.mts +2 -109
- package/dist/geometry/index.mjs +2 -327
- package/dist/index.d.mts +18 -20
- package/dist/index.mjs +27 -18
- package/dist/model/connection/deployment/index.d.mts +12 -15
- package/dist/model/connection/deployment/index.mjs +9 -2
- package/dist/model/connection/index.d.mts +14 -75
- package/dist/model/connection/index.mjs +10 -3
- package/dist/model/connection/model/index.d.mts +12 -15
- package/dist/model/connection/model/index.mjs +8 -2
- package/dist/model/index.d.mts +15 -57
- package/dist/model/index.mjs +11 -4
- package/dist/styles/index.d.mts +9 -117
- package/dist/styles/index.mjs +26 -59
- package/dist/types/RichText.d.mts +3 -0
- package/dist/types/RichText.mjs +7 -0
- package/dist/types/_aux.d.mts +3 -4
- package/dist/types/_aux.mjs +2 -7
- package/dist/types/expression-model.d.mts +6 -190
- package/dist/types/expression-model.mjs +6 -108
- package/dist/types/expression.d.mts +5 -6
- package/dist/types/expression.mjs +6 -112
- package/dist/types/fqnRef.d.mts +4 -60
- package/dist/types/fqnRef.mjs +6 -44
- package/dist/types/index.d.mts +9 -47
- package/dist/types/index.mjs +14 -16
- package/dist/types/scalar.d.mts +2 -2
- package/dist/types/scalar.mjs +7 -4
- package/dist/utils/graphology/index.d.mts +2 -1864
- package/dist/utils/graphology/index.mjs +3 -1
- package/dist/utils/index.d.mts +11 -124
- package/dist/utils/index.mjs +16 -43
- package/dist/utils/iterable/index.d.mts +9 -132
- package/dist/utils/iterable/index.mjs +2 -61
- package/dist/utils/mnemonist.d.mts +2 -0
- package/dist/utils/mnemonist.mjs +12 -0
- package/package.json +18 -22
- package/compute-view/relationships/package.json +0 -4
- package/dist/shared/core.3ZhnKAlZ.mjs +0 -106
- package/dist/shared/core.78IWAHAE.mjs +0 -332
- package/dist/shared/core.B85MJLTf.mjs +0 -5
- package/dist/shared/core.BFd1lU9R.mjs +0 -29
- package/dist/shared/core.BKlholso.mjs +0 -3
- package/dist/shared/core.BPGOjone.d.mts +0 -46
- package/dist/shared/core.BYdtmqY5.mjs +0 -1255
- package/dist/shared/core.BZCgljZd.d.mts +0 -83
- package/dist/shared/core.BbE4y-yl.mjs +0 -177
- package/dist/shared/core.BhNcrwzK.mjs +0 -7
- package/dist/shared/core.BkVA6gjl.mjs +0 -5
- package/dist/shared/core.BoYfZOBm.mjs +0 -3
- package/dist/shared/core.BsSXS-_R.mjs +0 -7
- package/dist/shared/core.BspBF3SJ.mjs +0 -3944
- package/dist/shared/core.BvPm_-u3.d.mts +0 -334
- package/dist/shared/core.C-Jdx7xo.d.mts +0 -1263
- package/dist/shared/core.C3JowhaZ.mjs +0 -32
- package/dist/shared/core.C4x8aaWa.mjs +0 -27
- package/dist/shared/core.CBLX4mT8.mjs +0 -28411
- package/dist/shared/core.CLSZtnFh.mjs +0 -69
- package/dist/shared/core.CRhT4Iio.mjs +0 -785
- package/dist/shared/core.CbT8JVYL.mjs +0 -5
- package/dist/shared/core.CdNCAwq7.mjs +0 -176
- package/dist/shared/core.ClTP6-TN.d.mts +0 -87
- package/dist/shared/core.CmGY9blO.mjs +0 -6717
- package/dist/shared/core.CmPXpJ8l.mjs +0 -21
- package/dist/shared/core.Coi_LOUZ.mjs +0 -7
- package/dist/shared/core.CqAJJpK_.mjs +0 -3
- package/dist/shared/core.Ctj2S3bD.d.mts +0 -466
- package/dist/shared/core.CywrQs86.d.mts +0 -31
- package/dist/shared/core.D00YbOhQ.d.mts +0 -300
- package/dist/shared/core.D0WZutNd.mjs +0 -94
- package/dist/shared/core.D4npX2q8.mjs +0 -18
- package/dist/shared/core.DB0Ue5Wx.d.mts +0 -70
- package/dist/shared/core.DEK-BO3_.mjs +0 -1206
- package/dist/shared/core.DH7TXqXf.mjs +0 -136
- package/dist/shared/core.DRJma0Ol.mjs +0 -22
- package/dist/shared/core.DSalPD6v.mjs +0 -1352
- package/dist/shared/core.DVfPJPJj.mjs +0 -2200
- package/dist/shared/core.DXhDWKFZ.d.mts +0 -3
- package/dist/shared/core.DbfO90vF.mjs +0 -434
- package/dist/shared/core.DcS-0zys.mjs +0 -10
- package/dist/shared/core.DeDCYTDR.d.mts +0 -98
- package/dist/shared/core.DpKY_3NF.d.mts +0 -504
- package/dist/shared/core.Dt0v3djy.d.mts +0 -41
- package/dist/shared/core.FZ305VYd.mjs +0 -21
- package/dist/shared/core.RfnaEmgA.mjs +0 -7
- package/dist/shared/core.oSV6SRfk.mjs +0 -13
- package/dist/shared/core.om36dXXW.mjs +0 -41
- package/dist/shared/core.q10OP9oJ.mjs +0 -5
- package/dist/shared/core.uFk_o9X6.mjs +0 -13
- package/dist/shared/core.xeuDd__E.mjs +0 -300
- package/dist/shared/core.xgoPk2RG.mjs +0 -4989
|
@@ -1,2200 +0,0 @@
|
|
|
1
|
-
import { t as t$3, e as e$3, a as DeploymentNodeModel, b as DeployedInstanceModel, c as DeploymentRelationModel, N as NestedElementOfDeployedInstanceModel } from './core.CRhT4Iio.mjs';
|
|
2
|
-
import { e as e$2 } from './core.BKlholso.mjs';
|
|
3
|
-
import { n as n$1 } from './core.BhNcrwzK.mjs';
|
|
4
|
-
import { c as splitGlobalFqn, h as extractStep, g as isStepEdgeId, i as isGroupElementKind, b as isGlobalFqn, a as GlobalFqn } from './core.D0WZutNd.mjs';
|
|
5
|
-
import { m as hierarchyLevel, n as nameFromFqn, i as isAncestor, g as commonAncestor, s as sortNaturalByFqn, f as sortParentsFirst, p as parentFqn, a as ancestorsFqn } from './core.CdNCAwq7.mjs';
|
|
6
|
-
import { m as memoizeProp } from './core.CLSZtnFh.mjs';
|
|
7
|
-
import { R as RichText, e as exact } from './core.DEK-BO3_.mjs';
|
|
8
|
-
import { preferSummary, preferDescription } from '../types/_aux.mjs';
|
|
9
|
-
import { i as ihead } from './core.uFk_o9X6.mjs';
|
|
10
|
-
import { t as t$6, b as compareNaturalHierarchically, c as compareNatural } from './core.3ZhnKAlZ.mjs';
|
|
11
|
-
import { t as t$2, a as t$4 } from './core.BsSXS-_R.mjs';
|
|
12
|
-
import { t as t$5, e as e$4 } from './core.Coi_LOUZ.mjs';
|
|
13
|
-
import { t as t$8 } from './core.CbT8JVYL.mjs';
|
|
14
|
-
import { t as t$7 } from './core.BkVA6gjl.mjs';
|
|
15
|
-
import { c as calcDriftsFromSnapshot, a as applyManualLayout, h as t$9 } from './core.DSalPD6v.mjs';
|
|
16
|
-
import { L as LikeC4Styles } from './core.BspBF3SJ.mjs';
|
|
17
|
-
import { g as getOrCreate } from './core.DcS-0zys.mjs';
|
|
18
|
-
import { D as DefaultMap } from './core.BbE4y-yl.mjs';
|
|
19
|
-
import { i as invariant, a as nonNullable } from './core.D4npX2q8.mjs';
|
|
20
|
-
import { FqnRef } from '../types/fqnRef.mjs';
|
|
21
|
-
import { c as isDeploymentNode, a as _type, _ as _stage, i as isOnStage, w as whereOperatorAsPredicate } from './core.DH7TXqXf.mjs';
|
|
22
|
-
import { i as ifind } from './core.CmPXpJ8l.mjs';
|
|
23
|
-
import { i as ifilter } from './core.DRJma0Ol.mjs';
|
|
24
|
-
import './core.BFd1lU9R.mjs';
|
|
25
|
-
import './core.78IWAHAE.mjs';
|
|
26
|
-
|
|
27
|
-
function e$1(e){if(e==null||e===``)return true;if(typeof e!=`object`)return false;if(`length`in e&&typeof e.length==`number`)return e.length===0;if(`size`in e&&typeof e.size==`number`)return e.size===0;for(let t in e)return false;return Object.getOwnPropertySymbols(e).length===0}
|
|
28
|
-
|
|
29
|
-
function t$1(...t){return t$2(n,t)}const n=e=>e.at(-1);
|
|
30
|
-
|
|
31
|
-
function e(e,t,n){return typeof t==`number`||t===void 0?n=>n.split(e,t):e.split(t,n)}
|
|
32
|
-
|
|
33
|
-
function t(...t){return t$2(Object.values,t)}
|
|
34
|
-
|
|
35
|
-
class ElementModel {
|
|
36
|
-
constructor($model, $element) {
|
|
37
|
-
this.$model = $model;
|
|
38
|
-
this.$element = $element;
|
|
39
|
-
this.id = this.$element.id;
|
|
40
|
-
this._literalId = this.$element.id;
|
|
41
|
-
const [projectId, fqn] = splitGlobalFqn(this.id);
|
|
42
|
-
if (projectId) {
|
|
43
|
-
this.imported = {
|
|
44
|
-
from: projectId,
|
|
45
|
-
fqn
|
|
46
|
-
};
|
|
47
|
-
this.hierarchyLevel = hierarchyLevel(fqn);
|
|
48
|
-
} else {
|
|
49
|
-
this.imported = null;
|
|
50
|
-
this.hierarchyLevel = hierarchyLevel(this.id);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Don't use in runtime, only for type inference
|
|
55
|
-
*/
|
|
56
|
-
Aux;
|
|
57
|
-
id;
|
|
58
|
-
_literalId;
|
|
59
|
-
hierarchyLevel;
|
|
60
|
-
imported;
|
|
61
|
-
get name() {
|
|
62
|
-
return nameFromFqn(this.id);
|
|
63
|
-
}
|
|
64
|
-
get parent() {
|
|
65
|
-
return this.$model.parent(this);
|
|
66
|
-
}
|
|
67
|
-
get kind() {
|
|
68
|
-
return this.$element.kind;
|
|
69
|
-
}
|
|
70
|
-
get shape() {
|
|
71
|
-
return this.style.shape;
|
|
72
|
-
}
|
|
73
|
-
get color() {
|
|
74
|
-
return this.style.color;
|
|
75
|
-
}
|
|
76
|
-
get icon() {
|
|
77
|
-
return this.style.icon ?? null;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Returns all tags of the element.
|
|
81
|
-
* It includes tags from the element and its kind.
|
|
82
|
-
*/
|
|
83
|
-
get tags() {
|
|
84
|
-
return memoizeProp(this, /* @__PURE__ */ Symbol.for("tags"), () => {
|
|
85
|
-
return n$1([
|
|
86
|
-
...this.$element.tags ?? [],
|
|
87
|
-
...this.$model.specification.elements[this.$element.kind]?.tags ?? []
|
|
88
|
-
]);
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
get title() {
|
|
92
|
-
return this.$element.title;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Returns true if the element has a summary and a description
|
|
96
|
-
* (if one is missing - it falls back to another)
|
|
97
|
-
*/
|
|
98
|
-
get hasSummary() {
|
|
99
|
-
return !!this.$element.summary && !!this.$element.description && !t$3(this.$element.summary, this.$element.description);
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Short description of the element.
|
|
103
|
-
* Falls back to description if summary is not provided.
|
|
104
|
-
*/
|
|
105
|
-
get summary() {
|
|
106
|
-
return RichText.memoize(this, "summary", preferSummary(this.$element));
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Long description of the element.
|
|
110
|
-
* Falls back to summary if description is not provided.
|
|
111
|
-
*/
|
|
112
|
-
get description() {
|
|
113
|
-
return RichText.memoize(this, "description", preferDescription(this.$element));
|
|
114
|
-
}
|
|
115
|
-
get technology() {
|
|
116
|
-
return this.$element.technology ?? null;
|
|
117
|
-
}
|
|
118
|
-
get links() {
|
|
119
|
-
return this.$element.links ?? [];
|
|
120
|
-
}
|
|
121
|
-
get defaultView() {
|
|
122
|
-
return memoizeProp(this, /* @__PURE__ */ Symbol.for("defaultView"), () => ihead(this.scopedViews()) ?? null);
|
|
123
|
-
}
|
|
124
|
-
get isRoot() {
|
|
125
|
-
return this.parent === null;
|
|
126
|
-
}
|
|
127
|
-
get style() {
|
|
128
|
-
return memoizeProp(this, "style", () => exact({
|
|
129
|
-
shape: this.$model.$styles.defaults.shape,
|
|
130
|
-
color: this.$model.$styles.defaults.color,
|
|
131
|
-
border: this.$model.$styles.defaults.border,
|
|
132
|
-
opacity: this.$model.$styles.defaults.opacity,
|
|
133
|
-
size: this.$model.$styles.defaults.size,
|
|
134
|
-
padding: this.$model.$styles.defaults.padding,
|
|
135
|
-
textSize: this.$model.$styles.defaults.text,
|
|
136
|
-
...this.$element.style
|
|
137
|
-
}));
|
|
138
|
-
}
|
|
139
|
-
isAncestorOf(another) {
|
|
140
|
-
return isAncestor(this, another);
|
|
141
|
-
}
|
|
142
|
-
isDescendantOf(another) {
|
|
143
|
-
return isAncestor(another, this);
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Get all ancestor elements (i.e. parent, parent’s parent, etc.)
|
|
147
|
-
* (from closest to root)
|
|
148
|
-
*/
|
|
149
|
-
ancestors() {
|
|
150
|
-
return this.$model.ancestors(this);
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Returns the common ancestor of this element and another element.
|
|
154
|
-
*/
|
|
155
|
-
commonAncestor(another) {
|
|
156
|
-
const common = commonAncestor(this.id, another.id);
|
|
157
|
-
return common ? this.$model.element(common) : null;
|
|
158
|
-
}
|
|
159
|
-
children() {
|
|
160
|
-
return this.$model.children(this);
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Get all descendant elements (i.e. children, children’s children, etc.)
|
|
164
|
-
*/
|
|
165
|
-
descendants(sort) {
|
|
166
|
-
if (sort) {
|
|
167
|
-
const sorted = sortNaturalByFqn([...this.$model.descendants(this)], sort);
|
|
168
|
-
return sorted[Symbol.iterator]();
|
|
169
|
-
}
|
|
170
|
-
return this.$model.descendants(this);
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Get all sibling (i.e. same parent)
|
|
174
|
-
*/
|
|
175
|
-
siblings() {
|
|
176
|
-
return this.$model.siblings(this);
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Resolve siblings of the element and its ancestors
|
|
180
|
-
* (from closest parent to root)
|
|
181
|
-
*/
|
|
182
|
-
*ascendingSiblings() {
|
|
183
|
-
yield* this.siblings();
|
|
184
|
-
for (const ancestor of this.ancestors()) {
|
|
185
|
-
yield* ancestor.siblings();
|
|
186
|
-
}
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* Resolve siblings of the element and its ancestors
|
|
191
|
-
* (from root to closest)
|
|
192
|
-
*/
|
|
193
|
-
*descendingSiblings() {
|
|
194
|
-
for (const ancestor of [...this.ancestors()].reverse()) {
|
|
195
|
-
yield* ancestor.siblings();
|
|
196
|
-
}
|
|
197
|
-
yield* this.siblings();
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
incoming(filter = "all") {
|
|
201
|
-
return this.$model.incoming(this, filter);
|
|
202
|
-
}
|
|
203
|
-
*incomers(filter = "all") {
|
|
204
|
-
const unique2 = /* @__PURE__ */ new Set();
|
|
205
|
-
for (const r of this.incoming(filter)) {
|
|
206
|
-
if (unique2.has(r.source.id)) {
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
unique2.add(r.source.id);
|
|
210
|
-
yield r.source;
|
|
211
|
-
}
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
outgoing(filter = "all") {
|
|
215
|
-
return this.$model.outgoing(this, filter);
|
|
216
|
-
}
|
|
217
|
-
*outgoers(filter = "all") {
|
|
218
|
-
const unique2 = /* @__PURE__ */ new Set();
|
|
219
|
-
for (const r of this.outgoing(filter)) {
|
|
220
|
-
if (unique2.has(r.target.id)) {
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
unique2.add(r.target.id);
|
|
224
|
-
yield r.target;
|
|
225
|
-
}
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
get allOutgoing() {
|
|
229
|
-
return memoizeProp(this, /* @__PURE__ */ Symbol.for("allOutgoing"), () => new Set(this.outgoing()));
|
|
230
|
-
}
|
|
231
|
-
get allIncoming() {
|
|
232
|
-
return memoizeProp(this, /* @__PURE__ */ Symbol.for("allIncoming"), () => new Set(this.incoming()));
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* Iterate over all views that include this element.
|
|
236
|
-
*/
|
|
237
|
-
views() {
|
|
238
|
-
return memoizeProp(this, /* @__PURE__ */ Symbol.for("views"), () => {
|
|
239
|
-
const views = /* @__PURE__ */ new Set();
|
|
240
|
-
for (const view of this.$model.views()) {
|
|
241
|
-
if (view.includesElement(this.id)) {
|
|
242
|
-
views.add(view);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
return views;
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Iterate over all views that scope this element.
|
|
250
|
-
* It is possible that element is not included in the view.
|
|
251
|
-
*/
|
|
252
|
-
scopedViews() {
|
|
253
|
-
return memoizeProp(this, /* @__PURE__ */ Symbol.for("scopedViews"), () => {
|
|
254
|
-
const views = /* @__PURE__ */ new Set();
|
|
255
|
-
for (const vm of this.$model.views()) {
|
|
256
|
-
if (vm.isScopedElementView() && vm.viewOf.id === this.id) {
|
|
257
|
-
views.add(vm);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
return views;
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
/**
|
|
264
|
-
* @returns true if the element is deployed
|
|
265
|
-
*/
|
|
266
|
-
isDeployed() {
|
|
267
|
-
return e$2(ihead(this.deployments()));
|
|
268
|
-
}
|
|
269
|
-
deployments() {
|
|
270
|
-
return this.$model.deployment.instancesOf(this);
|
|
271
|
-
}
|
|
272
|
-
hasMetadata() {
|
|
273
|
-
return !!this.$element.metadata && !e$3(this.$element.metadata);
|
|
274
|
-
}
|
|
275
|
-
getMetadata(field) {
|
|
276
|
-
if (field) {
|
|
277
|
-
return this.$element.metadata?.[field];
|
|
278
|
-
}
|
|
279
|
-
return this.$element.metadata ?? {};
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Checks if the element has the given tag.
|
|
283
|
-
*/
|
|
284
|
-
isTagged(tag) {
|
|
285
|
-
return this.tags.includes(tag);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const getId = (element) => {
|
|
290
|
-
return typeof element === "string" ? element : element.id;
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
const VIEW_FOLDERS_SEPARATOR = "/";
|
|
294
|
-
const splitViewTitle = (title) => {
|
|
295
|
-
invariant(!title.includes("\n"), "View title cannot contain newlines");
|
|
296
|
-
if (title.includes(VIEW_FOLDERS_SEPARATOR)) {
|
|
297
|
-
const segments = title.split(VIEW_FOLDERS_SEPARATOR).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
298
|
-
if (t$4(segments, 1)) {
|
|
299
|
-
return segments;
|
|
300
|
-
}
|
|
301
|
-
return [""];
|
|
302
|
-
}
|
|
303
|
-
return [title.trim()];
|
|
304
|
-
};
|
|
305
|
-
const normalizeViewPath = (title) => {
|
|
306
|
-
return splitViewTitle(title).join(VIEW_FOLDERS_SEPARATOR);
|
|
307
|
-
};
|
|
308
|
-
const getViewFolderPath = (title) => {
|
|
309
|
-
const segments = splitViewTitle(title);
|
|
310
|
-
if (!t$4(segments, 2)) {
|
|
311
|
-
return null;
|
|
312
|
-
}
|
|
313
|
-
return segments.slice(0, -1).join(VIEW_FOLDERS_SEPARATOR);
|
|
314
|
-
};
|
|
315
|
-
const extractViewTitleFromPath = (title) => {
|
|
316
|
-
if (!title.includes(VIEW_FOLDERS_SEPARATOR)) {
|
|
317
|
-
return title.trim();
|
|
318
|
-
}
|
|
319
|
-
return splitViewTitle(title).pop() ?? title;
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
class LikeC4DeploymentModel {
|
|
323
|
-
constructor($model) {
|
|
324
|
-
this.$model = $model;
|
|
325
|
-
const $deployments = this.$deployments = $model.$data.deployments;
|
|
326
|
-
const elements = t($deployments.elements);
|
|
327
|
-
for (const element of sortParentsFirst(elements)) {
|
|
328
|
-
const el = this.addElement(element);
|
|
329
|
-
for (const tag of el.tags) {
|
|
330
|
-
this.#allTags.get(tag).add(el);
|
|
331
|
-
}
|
|
332
|
-
if (el.isInstance()) {
|
|
333
|
-
this.#instancesOf.get(el.element.id).add(el);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
for (const relation of t($deployments.relations)) {
|
|
337
|
-
const el = this.addRelation(relation);
|
|
338
|
-
for (const tag of el.tags) {
|
|
339
|
-
this.#allTags.get(tag).add(el);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
#elements = /* @__PURE__ */ new Map();
|
|
344
|
-
// Parent element for given FQN
|
|
345
|
-
#parents = /* @__PURE__ */ new Map();
|
|
346
|
-
// Children elements for given FQN
|
|
347
|
-
#children = new DefaultMap(() => /* @__PURE__ */ new Set());
|
|
348
|
-
// Keep track of instances of the logical element
|
|
349
|
-
#instancesOf = new DefaultMap(() => /* @__PURE__ */ new Set());
|
|
350
|
-
#rootElements = /* @__PURE__ */ new Set();
|
|
351
|
-
#relations = /* @__PURE__ */ new Map();
|
|
352
|
-
// Incoming to an element or its descendants
|
|
353
|
-
#incoming = new DefaultMap(() => /* @__PURE__ */ new Set());
|
|
354
|
-
// Outgoing from an element or its descendants
|
|
355
|
-
#outgoing = new DefaultMap(() => /* @__PURE__ */ new Set());
|
|
356
|
-
// Relationships inside the element, among descendants
|
|
357
|
-
#internal = new DefaultMap(() => /* @__PURE__ */ new Set());
|
|
358
|
-
// readonly #views = new Map<ViewID, LikeC4ViewModel<A>>()
|
|
359
|
-
#allTags = new DefaultMap(
|
|
360
|
-
() => /* @__PURE__ */ new Set()
|
|
361
|
-
);
|
|
362
|
-
#nestedElementsOfDeployment = /* @__PURE__ */ new Map();
|
|
363
|
-
$deployments;
|
|
364
|
-
get $styles() {
|
|
365
|
-
return this.$model.$styles;
|
|
366
|
-
}
|
|
367
|
-
element(el) {
|
|
368
|
-
if (el instanceof DeploymentNodeModel || el instanceof DeployedInstanceModel) {
|
|
369
|
-
return el;
|
|
370
|
-
}
|
|
371
|
-
const id = getId(el);
|
|
372
|
-
return nonNullable(this.#elements.get(id), `Element ${id} not found`);
|
|
373
|
-
}
|
|
374
|
-
findElement(el) {
|
|
375
|
-
return this.#elements.get(el) ?? null;
|
|
376
|
-
}
|
|
377
|
-
node(el) {
|
|
378
|
-
const element = this.element(el);
|
|
379
|
-
invariant(element.isDeploymentNode(), `Element ${element.id} is not a deployment node`);
|
|
380
|
-
return element;
|
|
381
|
-
}
|
|
382
|
-
findNode(el) {
|
|
383
|
-
const element = this.findElement(el);
|
|
384
|
-
if (!element) {
|
|
385
|
-
return null;
|
|
386
|
-
}
|
|
387
|
-
invariant(element.isDeploymentNode(), `Element ${element?.id} is not a deployment node`);
|
|
388
|
-
return element;
|
|
389
|
-
}
|
|
390
|
-
instance(el) {
|
|
391
|
-
const element = this.element(el);
|
|
392
|
-
invariant(element.isInstance(), `Element ${element.id} is not a deployed instance`);
|
|
393
|
-
return element;
|
|
394
|
-
}
|
|
395
|
-
findInstance(el) {
|
|
396
|
-
const element = this.findElement(el);
|
|
397
|
-
if (!element) {
|
|
398
|
-
return null;
|
|
399
|
-
}
|
|
400
|
-
invariant(element.isInstance(), `Element ${element?.id} is not a deployed instance`);
|
|
401
|
-
return element;
|
|
402
|
-
}
|
|
403
|
-
/**
|
|
404
|
-
* Returns the root elements of the model.
|
|
405
|
-
*/
|
|
406
|
-
roots() {
|
|
407
|
-
return this.#rootElements.values();
|
|
408
|
-
}
|
|
409
|
-
/**
|
|
410
|
-
* Returns all elements in the model.
|
|
411
|
-
*/
|
|
412
|
-
elements() {
|
|
413
|
-
return this.#elements.values();
|
|
414
|
-
}
|
|
415
|
-
/**
|
|
416
|
-
* Returns all elements in the model.
|
|
417
|
-
*/
|
|
418
|
-
*nodes() {
|
|
419
|
-
for (const element of this.#elements.values()) {
|
|
420
|
-
if (element.isDeploymentNode()) {
|
|
421
|
-
yield element;
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
*nodesOfKind(kind) {
|
|
427
|
-
for (const node of this.#elements.values()) {
|
|
428
|
-
if (node.isDeploymentNode() && node.kind === kind) {
|
|
429
|
-
yield node;
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
*instances() {
|
|
435
|
-
for (const element of this.#elements.values()) {
|
|
436
|
-
if (element.isInstance()) {
|
|
437
|
-
yield element;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
/**
|
|
443
|
-
* Iterate over all instances of the given logical element.
|
|
444
|
-
*/
|
|
445
|
-
*instancesOf(element) {
|
|
446
|
-
const id = getId(element);
|
|
447
|
-
const instances = this.#instancesOf.get(id);
|
|
448
|
-
if (instances) {
|
|
449
|
-
yield* instances;
|
|
450
|
-
}
|
|
451
|
-
return;
|
|
452
|
-
}
|
|
453
|
-
deploymentRef(ref) {
|
|
454
|
-
if (FqnRef.isInsideInstanceRef(ref)) {
|
|
455
|
-
const { deployment, element } = ref;
|
|
456
|
-
return getOrCreate(this.#nestedElementsOfDeployment, `${deployment}@${element}`, () => {
|
|
457
|
-
return new NestedElementOfDeployedInstanceModel(this.instance(deployment), this.$model.element(element));
|
|
458
|
-
});
|
|
459
|
-
}
|
|
460
|
-
return this.element(ref.deployment);
|
|
461
|
-
}
|
|
462
|
-
/**
|
|
463
|
-
* Returns all relationships in the model.
|
|
464
|
-
*/
|
|
465
|
-
relationships() {
|
|
466
|
-
return this.#relations.values();
|
|
467
|
-
}
|
|
468
|
-
/**
|
|
469
|
-
* Returns a specific relationship by its ID.
|
|
470
|
-
*/
|
|
471
|
-
relationship(id) {
|
|
472
|
-
const relationId = getId(id);
|
|
473
|
-
return nonNullable(this.#relations.get(relationId), `DeploymentRelationModel ${relationId} not found`);
|
|
474
|
-
}
|
|
475
|
-
findRelationship(id) {
|
|
476
|
-
return this.#relations.get(id) ?? null;
|
|
477
|
-
}
|
|
478
|
-
/**
|
|
479
|
-
* Returns all deployment views in the model.
|
|
480
|
-
*/
|
|
481
|
-
*views() {
|
|
482
|
-
for (const view of this.$model.views()) {
|
|
483
|
-
if (view.isDeploymentView()) {
|
|
484
|
-
yield view;
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
return;
|
|
488
|
-
}
|
|
489
|
-
/**
|
|
490
|
-
* Returns the parent element of given element.
|
|
491
|
-
* @see ancestors
|
|
492
|
-
*/
|
|
493
|
-
parent(element) {
|
|
494
|
-
const id = getId(element);
|
|
495
|
-
return this.#parents.get(id) || null;
|
|
496
|
-
}
|
|
497
|
-
/**
|
|
498
|
-
* Get all children of the element (only direct children),
|
|
499
|
-
* @see descendants
|
|
500
|
-
*/
|
|
501
|
-
children(element) {
|
|
502
|
-
const id = getId(element);
|
|
503
|
-
return this.#children.get(id);
|
|
504
|
-
}
|
|
505
|
-
/**
|
|
506
|
-
* Get all sibling (i.e. same parent)
|
|
507
|
-
*/
|
|
508
|
-
*siblings(element) {
|
|
509
|
-
const id = getId(element);
|
|
510
|
-
const siblings = this.parent(element)?.children() ?? this.roots();
|
|
511
|
-
for (const sibling of siblings) {
|
|
512
|
-
if (sibling.id !== id) {
|
|
513
|
-
yield sibling;
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
return;
|
|
517
|
-
}
|
|
518
|
-
/**
|
|
519
|
-
* Get all ancestor elements (i.e. parent, parent’s parent, etc.)
|
|
520
|
-
* (from closest to root)
|
|
521
|
-
*/
|
|
522
|
-
*ancestors(element) {
|
|
523
|
-
let id = getId(element);
|
|
524
|
-
let parent;
|
|
525
|
-
while (parent = this.#parents.get(id)) {
|
|
526
|
-
yield parent;
|
|
527
|
-
id = parent.id;
|
|
528
|
-
}
|
|
529
|
-
return;
|
|
530
|
-
}
|
|
531
|
-
/**
|
|
532
|
-
* Get all descendant elements (i.e. children, children’s children, etc.)
|
|
533
|
-
*/
|
|
534
|
-
*descendants(element, sort = "desc") {
|
|
535
|
-
for (const child of this.children(element)) {
|
|
536
|
-
if (sort === "asc") {
|
|
537
|
-
yield child;
|
|
538
|
-
yield* this.descendants(child.id);
|
|
539
|
-
} else {
|
|
540
|
-
yield* this.descendants(child.id);
|
|
541
|
-
yield child;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
return;
|
|
545
|
-
}
|
|
546
|
-
/**
|
|
547
|
-
* Incoming relationships to the element and its descendants
|
|
548
|
-
* @see incomers
|
|
549
|
-
*/
|
|
550
|
-
*incoming(element, filter = "all") {
|
|
551
|
-
const id = getId(element);
|
|
552
|
-
for (const rel of this.#incoming.get(id)) {
|
|
553
|
-
switch (true) {
|
|
554
|
-
case filter === "all":
|
|
555
|
-
case (filter === "direct" && rel.target.id === id):
|
|
556
|
-
case (filter === "to-descendants" && rel.target.id !== id):
|
|
557
|
-
yield rel;
|
|
558
|
-
break;
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
/**
|
|
564
|
-
* Outgoing relationships from the element and its descendants
|
|
565
|
-
* @see outgoers
|
|
566
|
-
*/
|
|
567
|
-
*outgoing(element, filter = "all") {
|
|
568
|
-
const id = getId(element);
|
|
569
|
-
for (const rel of this.#outgoing.get(id)) {
|
|
570
|
-
switch (true) {
|
|
571
|
-
case filter === "all":
|
|
572
|
-
case (filter === "direct" && rel.source.id === id):
|
|
573
|
-
case (filter === "from-descendants" && rel.source.id !== id):
|
|
574
|
-
yield rel;
|
|
575
|
-
break;
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
return;
|
|
579
|
-
}
|
|
580
|
-
addElement(element) {
|
|
581
|
-
if (this.#elements.has(element.id)) {
|
|
582
|
-
throw new Error(`Element ${element.id} already exists`);
|
|
583
|
-
}
|
|
584
|
-
const el = isDeploymentNode(element) ? new DeploymentNodeModel(this, Object.freeze(element)) : new DeployedInstanceModel(this, Object.freeze(element), this.$model.element(element.element));
|
|
585
|
-
this.#elements.set(el.id, el);
|
|
586
|
-
const parentId = parentFqn(el.id);
|
|
587
|
-
if (parentId) {
|
|
588
|
-
invariant(this.#elements.has(parentId), `Parent ${parentId} of ${el.id} not found`);
|
|
589
|
-
this.#parents.set(el.id, this.node(parentId));
|
|
590
|
-
this.#children.get(parentId).add(el);
|
|
591
|
-
} else {
|
|
592
|
-
invariant(el.isDeploymentNode(), `Root element ${el.id} is not a deployment node`);
|
|
593
|
-
this.#rootElements.add(el);
|
|
594
|
-
}
|
|
595
|
-
return el;
|
|
596
|
-
}
|
|
597
|
-
addRelation(relation) {
|
|
598
|
-
if (this.#relations.has(relation.id)) {
|
|
599
|
-
throw new Error(`Relation ${relation.id} already exists`);
|
|
600
|
-
}
|
|
601
|
-
const rel = new DeploymentRelationModel(
|
|
602
|
-
this,
|
|
603
|
-
Object.freeze(relation)
|
|
604
|
-
);
|
|
605
|
-
this.#relations.set(rel.id, rel);
|
|
606
|
-
this.#incoming.get(rel.target.id).add(rel);
|
|
607
|
-
this.#outgoing.get(rel.source.id).add(rel);
|
|
608
|
-
const relParent = rel.boundary?.id ?? null;
|
|
609
|
-
if (relParent) {
|
|
610
|
-
for (const ancestor of [relParent, ...ancestorsFqn(relParent)]) {
|
|
611
|
-
this.#internal.get(ancestor).add(rel);
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
for (const sourceAncestor of ancestorsFqn(rel.source.id)) {
|
|
615
|
-
if (sourceAncestor === relParent) {
|
|
616
|
-
break;
|
|
617
|
-
}
|
|
618
|
-
this.#outgoing.get(sourceAncestor).add(rel);
|
|
619
|
-
}
|
|
620
|
-
for (const targetAncestor of ancestorsFqn(rel.target.id)) {
|
|
621
|
-
if (targetAncestor === relParent) {
|
|
622
|
-
break;
|
|
623
|
-
}
|
|
624
|
-
this.#incoming.get(targetAncestor).add(rel);
|
|
625
|
-
}
|
|
626
|
-
return rel;
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
class RelationshipModel {
|
|
631
|
-
constructor(model, $relationship) {
|
|
632
|
-
this.model = model;
|
|
633
|
-
this.$relationship = $relationship;
|
|
634
|
-
this.source = model.element(FqnRef.flatten($relationship.source));
|
|
635
|
-
this.target = model.element(FqnRef.flatten($relationship.target));
|
|
636
|
-
const parent = commonAncestor(this.source.id, this.target.id);
|
|
637
|
-
this.boundary = parent ? this.model.element(parent) : null;
|
|
638
|
-
}
|
|
639
|
-
source;
|
|
640
|
-
target;
|
|
641
|
-
/**
|
|
642
|
-
* Common ancestor of the source and target elements.
|
|
643
|
-
* Represents the boundary of the Relation.
|
|
644
|
-
*/
|
|
645
|
-
boundary;
|
|
646
|
-
get id() {
|
|
647
|
-
return this.$relationship.id;
|
|
648
|
-
}
|
|
649
|
-
get expression() {
|
|
650
|
-
return `${this.source.id} -> ${this.target.id}`;
|
|
651
|
-
}
|
|
652
|
-
get title() {
|
|
653
|
-
if (!e$2(this.$relationship.title)) {
|
|
654
|
-
return null;
|
|
655
|
-
}
|
|
656
|
-
return this.$relationship.title;
|
|
657
|
-
}
|
|
658
|
-
get technology() {
|
|
659
|
-
if (!e$2(this.$relationship.technology)) {
|
|
660
|
-
return null;
|
|
661
|
-
}
|
|
662
|
-
return this.$relationship.technology;
|
|
663
|
-
}
|
|
664
|
-
/**
|
|
665
|
-
* Returns true if the relationship has a summary and a description
|
|
666
|
-
* (if one is missing - it falls back to another)
|
|
667
|
-
*/
|
|
668
|
-
get hasSummary() {
|
|
669
|
-
return !!this.$relationship.summary && !!this.$relationship.description && !t$3(this.$relationship.summary, this.$relationship.description);
|
|
670
|
-
}
|
|
671
|
-
/**
|
|
672
|
-
* Short description of the relationship.
|
|
673
|
-
* Falls back to description if summary is not provided.
|
|
674
|
-
*/
|
|
675
|
-
get summary() {
|
|
676
|
-
return RichText.memoize(this, "summary", preferSummary(this.$relationship));
|
|
677
|
-
}
|
|
678
|
-
/**
|
|
679
|
-
* Long description of the relationship.
|
|
680
|
-
* Falls back to summary if description is not provided.
|
|
681
|
-
*/
|
|
682
|
-
get description() {
|
|
683
|
-
return RichText.memoize(this, "description", preferDescription(this.$relationship));
|
|
684
|
-
}
|
|
685
|
-
get navigateTo() {
|
|
686
|
-
return this.$relationship.navigateTo ? this.model.view(this.$relationship.navigateTo) : null;
|
|
687
|
-
}
|
|
688
|
-
get tags() {
|
|
689
|
-
return this.$relationship.tags ?? [];
|
|
690
|
-
}
|
|
691
|
-
get kind() {
|
|
692
|
-
return this.$relationship.kind ?? null;
|
|
693
|
-
}
|
|
694
|
-
get links() {
|
|
695
|
-
return this.$relationship.links ?? [];
|
|
696
|
-
}
|
|
697
|
-
get color() {
|
|
698
|
-
return this.$relationship.color ?? this.model.$styles.defaults.relationship.color;
|
|
699
|
-
}
|
|
700
|
-
get line() {
|
|
701
|
-
return this.$relationship.line ?? this.model.$styles.defaults.relationship.line;
|
|
702
|
-
}
|
|
703
|
-
get head() {
|
|
704
|
-
return this.$relationship.head ?? this.model.$styles.defaults.relationship.arrow;
|
|
705
|
-
}
|
|
706
|
-
get tail() {
|
|
707
|
-
return this.$relationship.tail;
|
|
708
|
-
}
|
|
709
|
-
/**
|
|
710
|
-
* Iterate over all views that include this relationship.
|
|
711
|
-
*/
|
|
712
|
-
*views() {
|
|
713
|
-
for (const view of this.model.views()) {
|
|
714
|
-
if (view.includesRelation(this.id)) {
|
|
715
|
-
yield view;
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
return;
|
|
719
|
-
}
|
|
720
|
-
isDeploymentRelation() {
|
|
721
|
-
return false;
|
|
722
|
-
}
|
|
723
|
-
isModelRelation() {
|
|
724
|
-
return true;
|
|
725
|
-
}
|
|
726
|
-
hasMetadata() {
|
|
727
|
-
return !!this.$relationship.metadata && !e$3(this.$relationship.metadata);
|
|
728
|
-
}
|
|
729
|
-
getMetadata(field) {
|
|
730
|
-
if (field) {
|
|
731
|
-
return this.$relationship.metadata?.[field];
|
|
732
|
-
}
|
|
733
|
-
return this.$relationship.metadata ?? {};
|
|
734
|
-
}
|
|
735
|
-
/**
|
|
736
|
-
* Checks if the relationship has the given tag.
|
|
737
|
-
*/
|
|
738
|
-
isTagged(tag) {
|
|
739
|
-
return this.tags.includes(tag);
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
class EdgeModel {
|
|
744
|
-
constructor($viewModel, $edge, source, target) {
|
|
745
|
-
this.source = source;
|
|
746
|
-
this.target = target;
|
|
747
|
-
this.$viewModel = $viewModel;
|
|
748
|
-
this.$view = $viewModel.$view;
|
|
749
|
-
this.$edge = $edge;
|
|
750
|
-
}
|
|
751
|
-
Aux;
|
|
752
|
-
$viewModel;
|
|
753
|
-
$view;
|
|
754
|
-
$edge;
|
|
755
|
-
get id() {
|
|
756
|
-
return this.$edge.id;
|
|
757
|
-
}
|
|
758
|
-
get parent() {
|
|
759
|
-
return this.$edge.parent ? this.$viewModel.node(this.$edge.parent) : null;
|
|
760
|
-
}
|
|
761
|
-
get label() {
|
|
762
|
-
return this.$edge.label ?? null;
|
|
763
|
-
}
|
|
764
|
-
get description() {
|
|
765
|
-
return RichText.memoize(this, "description", this.$edge.description);
|
|
766
|
-
}
|
|
767
|
-
get technology() {
|
|
768
|
-
return this.$edge.technology ?? null;
|
|
769
|
-
}
|
|
770
|
-
hasParent() {
|
|
771
|
-
return this.$edge.parent !== null;
|
|
772
|
-
}
|
|
773
|
-
get tags() {
|
|
774
|
-
return this.$edge.tags ?? [];
|
|
775
|
-
}
|
|
776
|
-
get stepNumber() {
|
|
777
|
-
return this.isStep() ? extractStep(this.id) : null;
|
|
778
|
-
}
|
|
779
|
-
get navigateTo() {
|
|
780
|
-
return this.$edge.navigateTo ? this.$viewModel.$model.view(this.$edge.navigateTo) : null;
|
|
781
|
-
}
|
|
782
|
-
get color() {
|
|
783
|
-
return this.$edge.color;
|
|
784
|
-
}
|
|
785
|
-
get line() {
|
|
786
|
-
return this.$edge.line ?? this.$viewModel.$styles.defaults.relationship.line;
|
|
787
|
-
}
|
|
788
|
-
get head() {
|
|
789
|
-
return this.$edge.head ?? this.$viewModel.$styles.defaults.relationship.arrow;
|
|
790
|
-
}
|
|
791
|
-
get tail() {
|
|
792
|
-
return this.$edge.tail;
|
|
793
|
-
}
|
|
794
|
-
isStep() {
|
|
795
|
-
return isStepEdgeId(this.id);
|
|
796
|
-
}
|
|
797
|
-
*relationships(type) {
|
|
798
|
-
for (const id of this.$edge.relations) {
|
|
799
|
-
if (type) {
|
|
800
|
-
const rel = this.$viewModel.$model.findRelationship(id, type);
|
|
801
|
-
if (rel) {
|
|
802
|
-
yield rel;
|
|
803
|
-
}
|
|
804
|
-
} else {
|
|
805
|
-
yield this.$viewModel.$model.relationship(id);
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
return;
|
|
809
|
-
}
|
|
810
|
-
includesRelation(rel) {
|
|
811
|
-
const id = typeof rel === "string" ? rel : rel.id;
|
|
812
|
-
return this.$edge.relations.includes(id);
|
|
813
|
-
}
|
|
814
|
-
isTagged(tag) {
|
|
815
|
-
return this.tags.includes(tag);
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
class NodeModel {
|
|
820
|
-
Aux;
|
|
821
|
-
$viewModel;
|
|
822
|
-
$view;
|
|
823
|
-
$node;
|
|
824
|
-
constructor($viewModel, $node) {
|
|
825
|
-
this.$viewModel = $viewModel;
|
|
826
|
-
this.$view = $viewModel.$view;
|
|
827
|
-
this.$node = $node;
|
|
828
|
-
}
|
|
829
|
-
get id() {
|
|
830
|
-
return this.$node.id;
|
|
831
|
-
}
|
|
832
|
-
get title() {
|
|
833
|
-
return this.$node.title;
|
|
834
|
-
}
|
|
835
|
-
get kind() {
|
|
836
|
-
return this.$node.kind;
|
|
837
|
-
}
|
|
838
|
-
get description() {
|
|
839
|
-
return RichText.memoize(this, "description", this.$node.description);
|
|
840
|
-
}
|
|
841
|
-
get technology() {
|
|
842
|
-
return this.$node.technology ?? null;
|
|
843
|
-
}
|
|
844
|
-
get parent() {
|
|
845
|
-
return this.$node.parent ? this.$viewModel.node(this.$node.parent) : null;
|
|
846
|
-
}
|
|
847
|
-
get element() {
|
|
848
|
-
const modelRef = this.$node.modelRef;
|
|
849
|
-
return modelRef ? this.$viewModel.$model.element(modelRef) : null;
|
|
850
|
-
}
|
|
851
|
-
get deployment() {
|
|
852
|
-
const modelRef = this.$node.deploymentRef;
|
|
853
|
-
return modelRef ? this.$viewModel.$model.deployment.element(modelRef) : null;
|
|
854
|
-
}
|
|
855
|
-
get shape() {
|
|
856
|
-
return this.$node.shape;
|
|
857
|
-
}
|
|
858
|
-
get color() {
|
|
859
|
-
return this.$node.color;
|
|
860
|
-
}
|
|
861
|
-
get icon() {
|
|
862
|
-
return this.$node.icon ?? null;
|
|
863
|
-
}
|
|
864
|
-
get tags() {
|
|
865
|
-
return this.$node.tags;
|
|
866
|
-
}
|
|
867
|
-
get links() {
|
|
868
|
-
return this.$node.links ?? [];
|
|
869
|
-
}
|
|
870
|
-
get navigateTo() {
|
|
871
|
-
return this.$node.navigateTo ? this.$viewModel.$model.view(this.$node.navigateTo) : null;
|
|
872
|
-
}
|
|
873
|
-
get style() {
|
|
874
|
-
return this.$node.style;
|
|
875
|
-
}
|
|
876
|
-
get x() {
|
|
877
|
-
return "x" in this.$node ? this.$node.x : void 0;
|
|
878
|
-
}
|
|
879
|
-
get y() {
|
|
880
|
-
return "y" in this.$node ? this.$node.y : void 0;
|
|
881
|
-
}
|
|
882
|
-
get width() {
|
|
883
|
-
return "width" in this.$node ? this.$node.width : void 0;
|
|
884
|
-
}
|
|
885
|
-
get height() {
|
|
886
|
-
return "height" in this.$node ? this.$node.height : void 0;
|
|
887
|
-
}
|
|
888
|
-
children() {
|
|
889
|
-
return memoizeProp(this, "children", () => new Set(this.$node.children.map((child) => this.$viewModel.node(child))));
|
|
890
|
-
}
|
|
891
|
-
/**
|
|
892
|
-
* Get all ancestor elements (i.e. parent, parent’s parent, etc.)
|
|
893
|
-
* (from closest to root)
|
|
894
|
-
*/
|
|
895
|
-
*ancestors() {
|
|
896
|
-
let parent = this.parent;
|
|
897
|
-
while (parent) {
|
|
898
|
-
yield parent;
|
|
899
|
-
parent = parent.parent;
|
|
900
|
-
}
|
|
901
|
-
return;
|
|
902
|
-
}
|
|
903
|
-
*siblings() {
|
|
904
|
-
const siblings = this.parent?.children() ?? this.$viewModel.roots();
|
|
905
|
-
for (const sibling of siblings) {
|
|
906
|
-
if (sibling.id !== this.id) {
|
|
907
|
-
yield sibling;
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
return;
|
|
911
|
-
}
|
|
912
|
-
*incoming(filter = "all") {
|
|
913
|
-
for (const edgeId of this.$node.inEdges) {
|
|
914
|
-
const edge = this.$viewModel.edge(edgeId);
|
|
915
|
-
switch (true) {
|
|
916
|
-
case filter === "all":
|
|
917
|
-
case (filter === "direct" && edge.target.id === this.id):
|
|
918
|
-
case (filter === "to-descendants" && edge.target.id !== this.id):
|
|
919
|
-
yield edge;
|
|
920
|
-
break;
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
return;
|
|
924
|
-
}
|
|
925
|
-
*incomers(filter = "all") {
|
|
926
|
-
const unique = /* @__PURE__ */ new Set();
|
|
927
|
-
for (const r of this.incoming(filter)) {
|
|
928
|
-
if (unique.has(r.source.id)) {
|
|
929
|
-
continue;
|
|
930
|
-
}
|
|
931
|
-
unique.add(r.source.id);
|
|
932
|
-
yield r.source;
|
|
933
|
-
}
|
|
934
|
-
return;
|
|
935
|
-
}
|
|
936
|
-
*outgoing(filter = "all") {
|
|
937
|
-
for (const edgeId of this.$node.outEdges) {
|
|
938
|
-
const edge = this.$viewModel.edge(edgeId);
|
|
939
|
-
switch (true) {
|
|
940
|
-
case filter === "all":
|
|
941
|
-
case (filter === "direct" && edge.source.id === this.id):
|
|
942
|
-
case (filter === "from-descendants" && edge.source.id !== this.id):
|
|
943
|
-
yield edge;
|
|
944
|
-
break;
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
return;
|
|
948
|
-
}
|
|
949
|
-
*outgoers(filter = "all") {
|
|
950
|
-
const unique = /* @__PURE__ */ new Set();
|
|
951
|
-
for (const r of this.outgoing(filter)) {
|
|
952
|
-
if (unique.has(r.target.id)) {
|
|
953
|
-
continue;
|
|
954
|
-
}
|
|
955
|
-
unique.add(r.target.id);
|
|
956
|
-
yield r.target;
|
|
957
|
-
}
|
|
958
|
-
return;
|
|
959
|
-
}
|
|
960
|
-
isLayouted() {
|
|
961
|
-
return "width" in this.$node && "height" in this.$node;
|
|
962
|
-
}
|
|
963
|
-
hasChildren() {
|
|
964
|
-
return this.$node.children.length > 0;
|
|
965
|
-
}
|
|
966
|
-
hasParent() {
|
|
967
|
-
return this.$node.parent !== null;
|
|
968
|
-
}
|
|
969
|
-
/**
|
|
970
|
-
* Check if this node references to logical model element.
|
|
971
|
-
*/
|
|
972
|
-
hasElement() {
|
|
973
|
-
return e$2(this.$node.modelRef);
|
|
974
|
-
}
|
|
975
|
-
/**
|
|
976
|
-
* Check if this node references to deployment element (Node or Instance).
|
|
977
|
-
*/
|
|
978
|
-
hasDeployment() {
|
|
979
|
-
return e$2(this.$node.deploymentRef);
|
|
980
|
-
}
|
|
981
|
-
/**
|
|
982
|
-
* Check if this node references to deployed instance
|
|
983
|
-
* Deployed instance always references to element and deployment element.
|
|
984
|
-
*/
|
|
985
|
-
hasDeployedInstance() {
|
|
986
|
-
return this.hasElement() && this.hasDeployment();
|
|
987
|
-
}
|
|
988
|
-
isGroup() {
|
|
989
|
-
return isGroupElementKind(this.$node);
|
|
990
|
-
}
|
|
991
|
-
/**
|
|
992
|
-
* Checks if the node has the given tag.
|
|
993
|
-
*/
|
|
994
|
-
isTagged(tag) {
|
|
995
|
-
return this.tags.includes(tag);
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
class LikeC4ViewModel {
|
|
1000
|
-
/**
|
|
1001
|
-
* Don't use in runtime, only for type inference
|
|
1002
|
-
*/
|
|
1003
|
-
Aux;
|
|
1004
|
-
#view;
|
|
1005
|
-
#rootnodes = /* @__PURE__ */ new Set();
|
|
1006
|
-
#nodes = /* @__PURE__ */ new Map();
|
|
1007
|
-
#edges = /* @__PURE__ */ new Map();
|
|
1008
|
-
#includeElements = /* @__PURE__ */ new Set();
|
|
1009
|
-
#includeDeployments = /* @__PURE__ */ new Set();
|
|
1010
|
-
#includeRelations = /* @__PURE__ */ new Set();
|
|
1011
|
-
#allTags = new DefaultMap((_key) => /* @__PURE__ */ new Set());
|
|
1012
|
-
#manualLayoutSnapshot;
|
|
1013
|
-
id;
|
|
1014
|
-
/**
|
|
1015
|
-
* The model this view belongs to
|
|
1016
|
-
*/
|
|
1017
|
-
$model;
|
|
1018
|
-
/**
|
|
1019
|
-
* The title of the view
|
|
1020
|
-
*/
|
|
1021
|
-
title;
|
|
1022
|
-
/**
|
|
1023
|
-
* View folder this view belongs to.
|
|
1024
|
-
* If view is top-level, this is the root folder.
|
|
1025
|
-
*/
|
|
1026
|
-
folder;
|
|
1027
|
-
/**
|
|
1028
|
-
* Path to this view, processed by {@link normalizeViewPath}
|
|
1029
|
-
*
|
|
1030
|
-
* @example
|
|
1031
|
-
* "Group 1/Group 2/View"
|
|
1032
|
-
*/
|
|
1033
|
-
viewPath;
|
|
1034
|
-
constructor(model, folder, view, manualLayoutSnapshot) {
|
|
1035
|
-
this.$model = model;
|
|
1036
|
-
this.#view = view;
|
|
1037
|
-
this.id = view.id;
|
|
1038
|
-
this.folder = folder;
|
|
1039
|
-
this.#manualLayoutSnapshot = manualLayoutSnapshot;
|
|
1040
|
-
for (const node of this.#view.nodes) {
|
|
1041
|
-
const el = new NodeModel(this, Object.freeze(node));
|
|
1042
|
-
this.#nodes.set(node.id, el);
|
|
1043
|
-
if (!node.parent) {
|
|
1044
|
-
this.#rootnodes.add(el);
|
|
1045
|
-
}
|
|
1046
|
-
if (node.deploymentRef) {
|
|
1047
|
-
this.#includeDeployments.add(node.deploymentRef);
|
|
1048
|
-
}
|
|
1049
|
-
if (node.modelRef) {
|
|
1050
|
-
this.#includeElements.add(node.modelRef);
|
|
1051
|
-
}
|
|
1052
|
-
for (const tag of el.tags) {
|
|
1053
|
-
this.#allTags.get(tag).add(el);
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
for (const edge of this.#view.edges) {
|
|
1057
|
-
const edgeModel = new EdgeModel(
|
|
1058
|
-
this,
|
|
1059
|
-
Object.freeze(edge),
|
|
1060
|
-
this.node(edge.source),
|
|
1061
|
-
this.node(edge.target)
|
|
1062
|
-
);
|
|
1063
|
-
for (const tag of edgeModel.tags) {
|
|
1064
|
-
this.#allTags.get(tag).add(edgeModel);
|
|
1065
|
-
}
|
|
1066
|
-
for (const rel of edge.relations) {
|
|
1067
|
-
this.#includeRelations.add(rel);
|
|
1068
|
-
}
|
|
1069
|
-
this.#edges.set(edge.id, edgeModel);
|
|
1070
|
-
}
|
|
1071
|
-
this.title = view.title ? extractViewTitleFromPath(view.title) : null;
|
|
1072
|
-
this.viewPath = view.title ? normalizeViewPath(view.title) : view.id;
|
|
1073
|
-
}
|
|
1074
|
-
/**
|
|
1075
|
-
* Returns the styles configuration for the project.
|
|
1076
|
-
*/
|
|
1077
|
-
get $styles() {
|
|
1078
|
-
return this.$model.$styles;
|
|
1079
|
-
}
|
|
1080
|
-
get _type() {
|
|
1081
|
-
return this.#view[_type];
|
|
1082
|
-
}
|
|
1083
|
-
get stage() {
|
|
1084
|
-
return this.#view[_stage];
|
|
1085
|
-
}
|
|
1086
|
-
get bounds() {
|
|
1087
|
-
if ("bounds" in this.#view) {
|
|
1088
|
-
return this.#view.bounds;
|
|
1089
|
-
}
|
|
1090
|
-
if (this.#manualLayoutSnapshot) {
|
|
1091
|
-
return this.#manualLayoutSnapshot.bounds;
|
|
1092
|
-
}
|
|
1093
|
-
throw new Error("View is not layouted");
|
|
1094
|
-
}
|
|
1095
|
-
/**
|
|
1096
|
-
* Returns title if defined, otherwise returns title of the element it is based on, otherwise returns its {@link id}
|
|
1097
|
-
*/
|
|
1098
|
-
get titleOrId() {
|
|
1099
|
-
return this.title ?? this.viewOf?.title ?? this.id;
|
|
1100
|
-
}
|
|
1101
|
-
/**
|
|
1102
|
-
* Returns title if defined, otherwise returns `Untitled`.
|
|
1103
|
-
*/
|
|
1104
|
-
get titleOrUntitled() {
|
|
1105
|
-
return this.title ?? "Untitled";
|
|
1106
|
-
}
|
|
1107
|
-
/**
|
|
1108
|
-
* Returns path to this view as an array of groups and this view as the last element
|
|
1109
|
-
* If view is top-level, returns only this view.
|
|
1110
|
-
*
|
|
1111
|
-
* @example
|
|
1112
|
-
* viewPath = "Group 1/Group 2/View"
|
|
1113
|
-
*
|
|
1114
|
-
* breadcrumbs = [
|
|
1115
|
-
* "Group 1", // folder
|
|
1116
|
-
* "Group 1/Group 2", // folder
|
|
1117
|
-
* "Group 1/Group 2/View" // view
|
|
1118
|
-
* ]
|
|
1119
|
-
*/
|
|
1120
|
-
get breadcrumbs() {
|
|
1121
|
-
return memoizeProp(this, "breadcrumbs", () => {
|
|
1122
|
-
if (!this.folder.isRoot) {
|
|
1123
|
-
return [...this.folder.breadcrumbs, this];
|
|
1124
|
-
}
|
|
1125
|
-
return [this];
|
|
1126
|
-
});
|
|
1127
|
-
}
|
|
1128
|
-
get description() {
|
|
1129
|
-
return RichText.memoize(this, "description", this.#view.description);
|
|
1130
|
-
}
|
|
1131
|
-
get tags() {
|
|
1132
|
-
return this.#view.tags ?? [];
|
|
1133
|
-
}
|
|
1134
|
-
get links() {
|
|
1135
|
-
return this.#view.links ?? [];
|
|
1136
|
-
}
|
|
1137
|
-
get viewOf() {
|
|
1138
|
-
if (this.isElementView()) {
|
|
1139
|
-
const viewOf = this.#view.viewOf;
|
|
1140
|
-
return viewOf ? this.$model.element(viewOf) : null;
|
|
1141
|
-
}
|
|
1142
|
-
return null;
|
|
1143
|
-
}
|
|
1144
|
-
/**
|
|
1145
|
-
* Available for dynamic views only
|
|
1146
|
-
* throws error if view is not dynamic
|
|
1147
|
-
*/
|
|
1148
|
-
get mode() {
|
|
1149
|
-
if (this.isDynamicView()) {
|
|
1150
|
-
return this.#view.variant ?? "diagram";
|
|
1151
|
-
}
|
|
1152
|
-
return null;
|
|
1153
|
-
}
|
|
1154
|
-
/**
|
|
1155
|
-
* All tags from nodes and edges.
|
|
1156
|
-
*/
|
|
1157
|
-
get includedTags() {
|
|
1158
|
-
return [...this.#allTags.keys()];
|
|
1159
|
-
}
|
|
1160
|
-
/**
|
|
1161
|
-
* The original view from the model.
|
|
1162
|
-
* In case of layouted model, this is the latest auto-layouted view without manual changes applied
|
|
1163
|
-
* @see {@link $layouted} should be used for rendering in the UI
|
|
1164
|
-
*/
|
|
1165
|
-
get $view() {
|
|
1166
|
-
if (!this.isLayouted() || "drifts" in this.#view) {
|
|
1167
|
-
return this.#view;
|
|
1168
|
-
}
|
|
1169
|
-
const snapshot = this.#manualLayoutSnapshot;
|
|
1170
|
-
if (snapshot) {
|
|
1171
|
-
return memoizeProp(this, "withDriftReasons", () => {
|
|
1172
|
-
return calcDriftsFromSnapshot(this.#view, snapshot);
|
|
1173
|
-
});
|
|
1174
|
-
}
|
|
1175
|
-
return this.#view;
|
|
1176
|
-
}
|
|
1177
|
-
/**
|
|
1178
|
-
* Returns the view with manual layout applied if it exists, otherwise returns the original view
|
|
1179
|
-
* This should be used for rendering in the UI
|
|
1180
|
-
*/
|
|
1181
|
-
get $layouted() {
|
|
1182
|
-
if (!this.isLayouted()) {
|
|
1183
|
-
throw new Error("View is not layouted");
|
|
1184
|
-
}
|
|
1185
|
-
return this.$manual ?? this.#view;
|
|
1186
|
-
}
|
|
1187
|
-
get hasManualLayout() {
|
|
1188
|
-
return this.#manualLayoutSnapshot !== void 0;
|
|
1189
|
-
}
|
|
1190
|
-
get hasLayoutDrifts() {
|
|
1191
|
-
if (!this.isLayouted()) {
|
|
1192
|
-
return false;
|
|
1193
|
-
}
|
|
1194
|
-
const manualLayout = this.$manual;
|
|
1195
|
-
return !!manualLayout?.drifts && manualLayout.drifts.length > 0;
|
|
1196
|
-
}
|
|
1197
|
-
/**
|
|
1198
|
-
* If view has manual layout, returns it with manual layout applied
|
|
1199
|
-
*/
|
|
1200
|
-
get $manual() {
|
|
1201
|
-
if (!this.isLayouted()) {
|
|
1202
|
-
return null;
|
|
1203
|
-
}
|
|
1204
|
-
const snapshot = this.#manualLayoutSnapshot;
|
|
1205
|
-
if (snapshot) {
|
|
1206
|
-
return memoizeProp(this, "snapshotWithManualLayout", () => {
|
|
1207
|
-
return applyManualLayout(this.#view, snapshot);
|
|
1208
|
-
});
|
|
1209
|
-
}
|
|
1210
|
-
return null;
|
|
1211
|
-
}
|
|
1212
|
-
roots() {
|
|
1213
|
-
return this.#rootnodes.values();
|
|
1214
|
-
}
|
|
1215
|
-
/**
|
|
1216
|
-
* Iterate over all nodes that have children.
|
|
1217
|
-
*/
|
|
1218
|
-
*compounds() {
|
|
1219
|
-
for (const node of this.#nodes.values()) {
|
|
1220
|
-
if (node.hasChildren()) {
|
|
1221
|
-
yield node;
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
|
-
return;
|
|
1225
|
-
}
|
|
1226
|
-
/**
|
|
1227
|
-
* Get node by id.
|
|
1228
|
-
* @throws Error if node is not found.
|
|
1229
|
-
*/
|
|
1230
|
-
node(node) {
|
|
1231
|
-
const nodeId = getId(node);
|
|
1232
|
-
return nonNullable(this.#nodes.get(nodeId), `Node ${nodeId} not found in view ${this.#view.id}`);
|
|
1233
|
-
}
|
|
1234
|
-
/**
|
|
1235
|
-
* Find node by id.
|
|
1236
|
-
*/
|
|
1237
|
-
findNode(node) {
|
|
1238
|
-
return this.#nodes.get(getId(node)) ?? null;
|
|
1239
|
-
}
|
|
1240
|
-
findNodeWithElement(element) {
|
|
1241
|
-
const id = getId(element);
|
|
1242
|
-
if (!this.#includeElements.has(id)) {
|
|
1243
|
-
return null;
|
|
1244
|
-
}
|
|
1245
|
-
return ifind(
|
|
1246
|
-
this.#nodes.values(),
|
|
1247
|
-
(node) => node.hasElement() && node.element.id === id
|
|
1248
|
-
) ?? null;
|
|
1249
|
-
}
|
|
1250
|
-
/**
|
|
1251
|
-
* Iterate over all nodes.
|
|
1252
|
-
*/
|
|
1253
|
-
nodes() {
|
|
1254
|
-
return this.#nodes.values();
|
|
1255
|
-
}
|
|
1256
|
-
/**
|
|
1257
|
-
* Get edge by id, throws error if edge is not found.
|
|
1258
|
-
* Use {@link findEdge} if you are not sure if the edge exists.
|
|
1259
|
-
*
|
|
1260
|
-
* @param edge Edge or id
|
|
1261
|
-
* @returns {@link EdgeModel}
|
|
1262
|
-
*/
|
|
1263
|
-
edge(edge) {
|
|
1264
|
-
const edgeId = getId(edge);
|
|
1265
|
-
return nonNullable(this.#edges.get(edgeId), `Edge ${edgeId} not found in view ${this.#view.id}`);
|
|
1266
|
-
}
|
|
1267
|
-
/**
|
|
1268
|
-
* Find edge by id.
|
|
1269
|
-
* @param edge Edge or id
|
|
1270
|
-
* @returns {@link EdgeModel} or null if edge is not found
|
|
1271
|
-
*/
|
|
1272
|
-
findEdge(edge) {
|
|
1273
|
-
return this.#edges.get(getId(edge)) ?? null;
|
|
1274
|
-
}
|
|
1275
|
-
/**
|
|
1276
|
-
* Iterate over all edges.
|
|
1277
|
-
*/
|
|
1278
|
-
edges() {
|
|
1279
|
-
return this.#edges.values();
|
|
1280
|
-
}
|
|
1281
|
-
/**
|
|
1282
|
-
* Iterate over all edges.
|
|
1283
|
-
*/
|
|
1284
|
-
*edgesWithRelation(relation) {
|
|
1285
|
-
for (const edge of this.#edges.values()) {
|
|
1286
|
-
if (edge.includesRelation(relation)) {
|
|
1287
|
-
yield edge;
|
|
1288
|
-
}
|
|
1289
|
-
}
|
|
1290
|
-
return;
|
|
1291
|
-
}
|
|
1292
|
-
/**
|
|
1293
|
-
* Nodes that have references to elements from logical model.
|
|
1294
|
-
*/
|
|
1295
|
-
*elements() {
|
|
1296
|
-
for (const node of this.#nodes.values()) {
|
|
1297
|
-
if (node.hasElement()) {
|
|
1298
|
-
yield node;
|
|
1299
|
-
}
|
|
1300
|
-
}
|
|
1301
|
-
return;
|
|
1302
|
-
}
|
|
1303
|
-
/**
|
|
1304
|
-
* Checks if the view has the given tag.
|
|
1305
|
-
*/
|
|
1306
|
-
isTagged(tag) {
|
|
1307
|
-
return this.tags.includes(tag);
|
|
1308
|
-
}
|
|
1309
|
-
includesElement(element) {
|
|
1310
|
-
return this.#includeElements.has(getId(element));
|
|
1311
|
-
}
|
|
1312
|
-
includesDeployment(deployment) {
|
|
1313
|
-
return this.#includeDeployments.has(getId(deployment));
|
|
1314
|
-
}
|
|
1315
|
-
includesRelation(relation) {
|
|
1316
|
-
return this.#includeRelations.has(getId(relation));
|
|
1317
|
-
}
|
|
1318
|
-
/**
|
|
1319
|
-
* Below are type guards.
|
|
1320
|
-
*/
|
|
1321
|
-
isComputed() {
|
|
1322
|
-
return this.#view[_stage] === "computed";
|
|
1323
|
-
}
|
|
1324
|
-
isLayouted() {
|
|
1325
|
-
return this.#view[_stage] === "layouted";
|
|
1326
|
-
}
|
|
1327
|
-
/**
|
|
1328
|
-
* @deprecated Use {@link isLayouted} instead
|
|
1329
|
-
*/
|
|
1330
|
-
isDiagram() {
|
|
1331
|
-
return this.#view[_stage] === "layouted";
|
|
1332
|
-
}
|
|
1333
|
-
isElementView() {
|
|
1334
|
-
return this.#view[_type] === "element";
|
|
1335
|
-
}
|
|
1336
|
-
isScopedElementView() {
|
|
1337
|
-
return this.#view[_type] === "element" && e$2(this.#view.viewOf);
|
|
1338
|
-
}
|
|
1339
|
-
isDeploymentView() {
|
|
1340
|
-
return this.#view[_type] === "deployment";
|
|
1341
|
-
}
|
|
1342
|
-
isDynamicView() {
|
|
1343
|
-
return this.#view[_type] === "dynamic";
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
class LikeC4ViewsFolder {
|
|
1348
|
-
$model;
|
|
1349
|
-
/**
|
|
1350
|
-
* Path to this view folder, processed by {@link normalizeViewPath}
|
|
1351
|
-
*
|
|
1352
|
-
* @example
|
|
1353
|
-
* "Folder 1/Folder 2/Folder 3"
|
|
1354
|
-
*/
|
|
1355
|
-
path;
|
|
1356
|
-
/**
|
|
1357
|
-
* Title of this view folder.
|
|
1358
|
-
*
|
|
1359
|
-
* @example
|
|
1360
|
-
* // title is the last segment of the path
|
|
1361
|
-
* path = "Folder 1/Folder 2/Folder 3"
|
|
1362
|
-
* title = "Folder 3"
|
|
1363
|
-
*/
|
|
1364
|
-
title;
|
|
1365
|
-
/**
|
|
1366
|
-
* Whether this is the root view folder.
|
|
1367
|
-
*
|
|
1368
|
-
* !NOTE
|
|
1369
|
-
* Root folder is special folder with an empty path and used only for internal purposes. \
|
|
1370
|
-
* It is not visible to the user and should not be used in the code.
|
|
1371
|
-
*/
|
|
1372
|
-
isRoot;
|
|
1373
|
-
parentPath;
|
|
1374
|
-
defaultViewId;
|
|
1375
|
-
constructor($model, path, defaultViewId) {
|
|
1376
|
-
this.$model = $model;
|
|
1377
|
-
this.path = path.join("/");
|
|
1378
|
-
this.isRoot = this.path === "";
|
|
1379
|
-
this.title = t$1(path);
|
|
1380
|
-
if (this.isRoot) {
|
|
1381
|
-
this.parentPath = void 0;
|
|
1382
|
-
} else {
|
|
1383
|
-
this.parentPath = path.slice(0, -1).join("/");
|
|
1384
|
-
}
|
|
1385
|
-
this.defaultViewId = defaultViewId;
|
|
1386
|
-
}
|
|
1387
|
-
/**
|
|
1388
|
-
* Default view of this view folder.\
|
|
1389
|
-
* It is for the case when there is a view at the same path as this folder.\
|
|
1390
|
-
* (if there are multiple views at the same path, the first one is chosen)
|
|
1391
|
-
*
|
|
1392
|
-
* @example
|
|
1393
|
-
* ```
|
|
1394
|
-
* // Assume the following views exist:
|
|
1395
|
-
* const views = [
|
|
1396
|
-
* "Folder 1/ Folder 2 / View",
|
|
1397
|
-
* "Folder 1/ Folder 2 / View / Subview",
|
|
1398
|
-
* ]
|
|
1399
|
-
* ```
|
|
1400
|
-
* Group with path `Folder 1/ Folder 2 / View`\
|
|
1401
|
-
* will have default view `Folder 1/ Folder 2 / View`
|
|
1402
|
-
*/
|
|
1403
|
-
get defaultView() {
|
|
1404
|
-
if (!this.defaultViewId) {
|
|
1405
|
-
return null;
|
|
1406
|
-
}
|
|
1407
|
-
return this.$model.view(this.defaultViewId);
|
|
1408
|
-
}
|
|
1409
|
-
/**
|
|
1410
|
-
* Returns path to this view folder as an array of ancestors (excluding root) and this view folder as the last element
|
|
1411
|
-
*
|
|
1412
|
-
* @throws Error if this is the root folder.
|
|
1413
|
-
*/
|
|
1414
|
-
get breadcrumbs() {
|
|
1415
|
-
invariant(!this.isRoot, "Root view folder has no breadcrumbs");
|
|
1416
|
-
return memoizeProp(this, "breadcrumbs", () => {
|
|
1417
|
-
const parent = this.parent;
|
|
1418
|
-
if (parent) {
|
|
1419
|
-
if (parent.isRoot) {
|
|
1420
|
-
return [parent, this];
|
|
1421
|
-
}
|
|
1422
|
-
return [...parent.breadcrumbs, this];
|
|
1423
|
-
}
|
|
1424
|
-
return [this];
|
|
1425
|
-
});
|
|
1426
|
-
}
|
|
1427
|
-
/**
|
|
1428
|
-
* Returns parent folder
|
|
1429
|
-
*
|
|
1430
|
-
* @throws Error if this is the root folder.
|
|
1431
|
-
*/
|
|
1432
|
-
get parent() {
|
|
1433
|
-
invariant(!this.isRoot, "Root view folder has no parent");
|
|
1434
|
-
if (e$1(this.parentPath)) {
|
|
1435
|
-
return null;
|
|
1436
|
-
}
|
|
1437
|
-
return this.$model.viewFolder(this.parentPath);
|
|
1438
|
-
}
|
|
1439
|
-
/**
|
|
1440
|
-
* Returns sorted set of children
|
|
1441
|
-
* - First folders
|
|
1442
|
-
* - Then views
|
|
1443
|
-
*/
|
|
1444
|
-
get children() {
|
|
1445
|
-
return this.$model.viewFolderItems(this.path);
|
|
1446
|
-
}
|
|
1447
|
-
/**
|
|
1448
|
-
* Returns sorted array of children folders
|
|
1449
|
-
*/
|
|
1450
|
-
get folders() {
|
|
1451
|
-
return memoizeProp(this, "folders", () => {
|
|
1452
|
-
const folders = [];
|
|
1453
|
-
for (const child of this.children) {
|
|
1454
|
-
if (child instanceof LikeC4ViewsFolder) {
|
|
1455
|
-
folders.push(child);
|
|
1456
|
-
}
|
|
1457
|
-
}
|
|
1458
|
-
return folders;
|
|
1459
|
-
});
|
|
1460
|
-
}
|
|
1461
|
-
/**
|
|
1462
|
-
* Returns all views in this view folder.
|
|
1463
|
-
*/
|
|
1464
|
-
get views() {
|
|
1465
|
-
return memoizeProp(this, "views", () => {
|
|
1466
|
-
const views = [];
|
|
1467
|
-
for (const child of this.children) {
|
|
1468
|
-
if (child instanceof LikeC4ViewModel) {
|
|
1469
|
-
views.push(child);
|
|
1470
|
-
}
|
|
1471
|
-
}
|
|
1472
|
-
return views;
|
|
1473
|
-
});
|
|
1474
|
-
}
|
|
1475
|
-
}
|
|
1476
|
-
|
|
1477
|
-
class LikeC4Model {
|
|
1478
|
-
/**
|
|
1479
|
-
* Don't use in runtime, only for type inference
|
|
1480
|
-
*/
|
|
1481
|
-
Aux;
|
|
1482
|
-
_elements = /* @__PURE__ */ new Map();
|
|
1483
|
-
// Parent element for given FQN
|
|
1484
|
-
_parents = /* @__PURE__ */ new Map();
|
|
1485
|
-
// Children elements for given FQN
|
|
1486
|
-
_children = new DefaultMap(() => /* @__PURE__ */ new Set());
|
|
1487
|
-
_rootElements = /* @__PURE__ */ new Set();
|
|
1488
|
-
_relations = /* @__PURE__ */ new Map();
|
|
1489
|
-
// Incoming to an element or its descendants
|
|
1490
|
-
_incoming = new DefaultMap(() => /* @__PURE__ */ new Set());
|
|
1491
|
-
// Outgoing from an element or its descendants
|
|
1492
|
-
_outgoing = new DefaultMap(() => /* @__PURE__ */ new Set());
|
|
1493
|
-
// Relationships inside the element, among descendants
|
|
1494
|
-
_internal = new DefaultMap(() => /* @__PURE__ */ new Set());
|
|
1495
|
-
_views = /* @__PURE__ */ new Map();
|
|
1496
|
-
_rootViewFolder;
|
|
1497
|
-
_viewFolders = /* @__PURE__ */ new Map();
|
|
1498
|
-
_viewFolderItems = new DefaultMap(() => /* @__PURE__ */ new Set());
|
|
1499
|
-
_allTags = new DefaultMap(() => /* @__PURE__ */ new Set());
|
|
1500
|
-
static fromParsed(model) {
|
|
1501
|
-
return new LikeC4Model(model);
|
|
1502
|
-
}
|
|
1503
|
-
static create(model) {
|
|
1504
|
-
return new LikeC4Model(model);
|
|
1505
|
-
}
|
|
1506
|
-
/**
|
|
1507
|
-
* Creates a new LikeC4Model instance and infers types from a model dump.\
|
|
1508
|
-
* Model dump expected to be computed or layouted.
|
|
1509
|
-
*
|
|
1510
|
-
* @typeParam D - A constant type parameter extending LikeC4ModelDump
|
|
1511
|
-
* @param dump - The model dump to create the instance from
|
|
1512
|
-
* @returns A new LikeC4Model instance with types inferred from the dump
|
|
1513
|
-
*/
|
|
1514
|
-
static fromDump(dump) {
|
|
1515
|
-
const {
|
|
1516
|
-
_stage: stage = "layouted",
|
|
1517
|
-
projectId = "unknown",
|
|
1518
|
-
project,
|
|
1519
|
-
globals,
|
|
1520
|
-
imports,
|
|
1521
|
-
deployments,
|
|
1522
|
-
views,
|
|
1523
|
-
relations,
|
|
1524
|
-
elements,
|
|
1525
|
-
specification
|
|
1526
|
-
} = dump;
|
|
1527
|
-
return new LikeC4Model({
|
|
1528
|
-
[_stage]: stage,
|
|
1529
|
-
projectId,
|
|
1530
|
-
project,
|
|
1531
|
-
globals: {
|
|
1532
|
-
predicates: globals?.predicates ?? {},
|
|
1533
|
-
dynamicPredicates: globals?.dynamicPredicates ?? {},
|
|
1534
|
-
styles: globals?.styles ?? {}
|
|
1535
|
-
},
|
|
1536
|
-
imports: imports ?? {},
|
|
1537
|
-
deployments: {
|
|
1538
|
-
elements: deployments?.elements ?? {},
|
|
1539
|
-
relations: deployments?.relations ?? {}
|
|
1540
|
-
},
|
|
1541
|
-
views: views ?? {},
|
|
1542
|
-
relations: relations ?? {},
|
|
1543
|
-
elements: elements ?? {},
|
|
1544
|
-
specification
|
|
1545
|
-
});
|
|
1546
|
-
}
|
|
1547
|
-
deployment;
|
|
1548
|
-
$data;
|
|
1549
|
-
constructor($data) {
|
|
1550
|
-
this.$data = $data;
|
|
1551
|
-
for (const [, element] of t$5($data.elements)) {
|
|
1552
|
-
const el = this.addElement(element);
|
|
1553
|
-
for (const tag of el.tags) {
|
|
1554
|
-
this._allTags.get(tag).add(el);
|
|
1555
|
-
}
|
|
1556
|
-
}
|
|
1557
|
-
for (const [projectId, elements] of t$5($data.imports ?? {})) {
|
|
1558
|
-
for (const element of sortParentsFirst(elements)) {
|
|
1559
|
-
const el = this.addImportedElement(projectId, element);
|
|
1560
|
-
for (const tag of el.tags) {
|
|
1561
|
-
this._allTags.get(tag).add(el);
|
|
1562
|
-
}
|
|
1563
|
-
}
|
|
1564
|
-
}
|
|
1565
|
-
for (const relation of t($data.relations)) {
|
|
1566
|
-
const el = this.addRelation(relation);
|
|
1567
|
-
for (const tag of el.tags) {
|
|
1568
|
-
this._allTags.get(tag).add(el);
|
|
1569
|
-
}
|
|
1570
|
-
}
|
|
1571
|
-
this.deployment = new LikeC4DeploymentModel(this);
|
|
1572
|
-
if (isOnStage($data, "computed") || isOnStage($data, "layouted")) {
|
|
1573
|
-
const compare = compareNaturalHierarchically(VIEW_FOLDERS_SEPARATOR);
|
|
1574
|
-
const views = t$6(
|
|
1575
|
-
t($data.views),
|
|
1576
|
-
t$8((view) => ({
|
|
1577
|
-
view,
|
|
1578
|
-
path: normalizeViewPath(view.title ?? view.id),
|
|
1579
|
-
folderPath: view.title && getViewFolderPath(view.title) || ""
|
|
1580
|
-
})),
|
|
1581
|
-
// Sort hierarchically by groups, but keep same order within groups
|
|
1582
|
-
t$7((a, b) => compare(a.folderPath, b.folderPath))
|
|
1583
|
-
);
|
|
1584
|
-
const getOrCreateFolder = (path) => {
|
|
1585
|
-
let folder = this._viewFolders.get(path);
|
|
1586
|
-
if (!folder) {
|
|
1587
|
-
const segments = e(path, VIEW_FOLDERS_SEPARATOR);
|
|
1588
|
-
invariant(t$4(segments, 1), `View group path "${path}" must have at least one element`);
|
|
1589
|
-
let defaultView;
|
|
1590
|
-
if (path === "") {
|
|
1591
|
-
defaultView = views.find((view) => view.view.id === "index");
|
|
1592
|
-
} else {
|
|
1593
|
-
defaultView = views.find((view) => view.path === path);
|
|
1594
|
-
}
|
|
1595
|
-
folder = new LikeC4ViewsFolder(this, segments, defaultView?.view.id);
|
|
1596
|
-
this._viewFolders.set(path, folder);
|
|
1597
|
-
}
|
|
1598
|
-
return folder;
|
|
1599
|
-
};
|
|
1600
|
-
this._rootViewFolder = getOrCreateFolder("");
|
|
1601
|
-
for (const { folderPath } of views) {
|
|
1602
|
-
if (this._viewFolders.has(folderPath)) {
|
|
1603
|
-
continue;
|
|
1604
|
-
}
|
|
1605
|
-
e(folderPath, VIEW_FOLDERS_SEPARATOR).reduce((segments, segment) => {
|
|
1606
|
-
const parent = segments.join(VIEW_FOLDERS_SEPARATOR);
|
|
1607
|
-
const path = e$3(parent) ? segment : parent + VIEW_FOLDERS_SEPARATOR + segment;
|
|
1608
|
-
const folder = getOrCreateFolder(path);
|
|
1609
|
-
this._viewFolderItems.get(parent).add(folder);
|
|
1610
|
-
segments.push(segment);
|
|
1611
|
-
return segments;
|
|
1612
|
-
}, []);
|
|
1613
|
-
}
|
|
1614
|
-
for (const { view, folderPath } of views) {
|
|
1615
|
-
const vm = new LikeC4ViewModel(
|
|
1616
|
-
this,
|
|
1617
|
-
getOrCreateFolder(folderPath),
|
|
1618
|
-
view,
|
|
1619
|
-
$data.manualLayouts?.[view.id]
|
|
1620
|
-
);
|
|
1621
|
-
this._viewFolderItems.get(folderPath).add(vm);
|
|
1622
|
-
this._views.set(view.id, vm);
|
|
1623
|
-
for (const tag of vm.tags) {
|
|
1624
|
-
this._allTags.get(tag).add(vm);
|
|
1625
|
-
}
|
|
1626
|
-
}
|
|
1627
|
-
} else {
|
|
1628
|
-
this._rootViewFolder = new LikeC4ViewsFolder(this, [""], void 0);
|
|
1629
|
-
this._viewFolders.set(this._rootViewFolder.path, this._rootViewFolder);
|
|
1630
|
-
}
|
|
1631
|
-
}
|
|
1632
|
-
/**
|
|
1633
|
-
* Type narrows the model to the parsed stage.
|
|
1634
|
-
* This is useful for tests
|
|
1635
|
-
*/
|
|
1636
|
-
get asParsed() {
|
|
1637
|
-
return this;
|
|
1638
|
-
}
|
|
1639
|
-
/**
|
|
1640
|
-
* Type narrows the model to the layouted stage.
|
|
1641
|
-
* This is useful for tests
|
|
1642
|
-
*/
|
|
1643
|
-
get asComputed() {
|
|
1644
|
-
return this;
|
|
1645
|
-
}
|
|
1646
|
-
/**
|
|
1647
|
-
* Type narrows the model to the layouted stage.
|
|
1648
|
-
* This is useful for tests
|
|
1649
|
-
*/
|
|
1650
|
-
get asLayouted() {
|
|
1651
|
-
return this;
|
|
1652
|
-
}
|
|
1653
|
-
/**
|
|
1654
|
-
* Returns the styles configuration for the project.
|
|
1655
|
-
*/
|
|
1656
|
-
get $styles() {
|
|
1657
|
-
return memoizeProp(
|
|
1658
|
-
this,
|
|
1659
|
-
"styles",
|
|
1660
|
-
() => LikeC4Styles.from(
|
|
1661
|
-
this.$data.project.styles,
|
|
1662
|
-
this.$data.specification.customColors ? {
|
|
1663
|
-
theme: {
|
|
1664
|
-
colors: this.$data.specification.customColors
|
|
1665
|
-
}
|
|
1666
|
-
} : void 0
|
|
1667
|
-
)
|
|
1668
|
-
);
|
|
1669
|
-
}
|
|
1670
|
-
/**
|
|
1671
|
-
* Type guard the model to the parsed stage.
|
|
1672
|
-
*/
|
|
1673
|
-
isParsed() {
|
|
1674
|
-
return this.stage === "parsed";
|
|
1675
|
-
}
|
|
1676
|
-
/**
|
|
1677
|
-
* Type guard the model to the layouted stage.
|
|
1678
|
-
*/
|
|
1679
|
-
isLayouted() {
|
|
1680
|
-
return this.stage === "layouted";
|
|
1681
|
-
}
|
|
1682
|
-
/**
|
|
1683
|
-
* Type guard the model to the computed stage.
|
|
1684
|
-
*/
|
|
1685
|
-
isComputed() {
|
|
1686
|
-
return this.stage === "computed";
|
|
1687
|
-
}
|
|
1688
|
-
/**
|
|
1689
|
-
* Keeping here for backward compatibility
|
|
1690
|
-
* @deprecated use {@link $data}
|
|
1691
|
-
*/
|
|
1692
|
-
get $model() {
|
|
1693
|
-
return this.$data;
|
|
1694
|
-
}
|
|
1695
|
-
get stage() {
|
|
1696
|
-
return this.$data[_stage];
|
|
1697
|
-
}
|
|
1698
|
-
/**
|
|
1699
|
-
* Returns the Project ID associated with the model.
|
|
1700
|
-
* If the project ID is not defined in the model, it returns "default".
|
|
1701
|
-
*/
|
|
1702
|
-
get projectId() {
|
|
1703
|
-
return this.$data.projectId ?? "default";
|
|
1704
|
-
}
|
|
1705
|
-
/**
|
|
1706
|
-
* Returns the project associated with the model.
|
|
1707
|
-
* If the project is not defined in the model, it returns a default project with the ID "default".
|
|
1708
|
-
*/
|
|
1709
|
-
get project() {
|
|
1710
|
-
return this.$data.project ?? memoizeProp(this, /* @__PURE__ */ Symbol.for("project"), () => ({
|
|
1711
|
-
id: this.projectId
|
|
1712
|
-
}));
|
|
1713
|
-
}
|
|
1714
|
-
get specification() {
|
|
1715
|
-
return this.$data.specification;
|
|
1716
|
-
}
|
|
1717
|
-
get globals() {
|
|
1718
|
-
return memoizeProp(this, /* @__PURE__ */ Symbol.for("globals"), () => ({
|
|
1719
|
-
predicates: {
|
|
1720
|
-
...this.$data.globals?.predicates
|
|
1721
|
-
},
|
|
1722
|
-
dynamicPredicates: {
|
|
1723
|
-
...this.$data.globals?.dynamicPredicates
|
|
1724
|
-
},
|
|
1725
|
-
styles: {
|
|
1726
|
-
...this.$data.globals?.styles
|
|
1727
|
-
}
|
|
1728
|
-
}));
|
|
1729
|
-
}
|
|
1730
|
-
/**
|
|
1731
|
-
* Returns the element with the given FQN.
|
|
1732
|
-
*
|
|
1733
|
-
* @throws Error if element is not found\
|
|
1734
|
-
* Use {@link findElement} if you don't want to throw an error
|
|
1735
|
-
*
|
|
1736
|
-
* @note Method is type-safe for typed model
|
|
1737
|
-
|
|
1738
|
-
* @example
|
|
1739
|
-
* model.element('cloud.frontend')
|
|
1740
|
-
* // or object with id property of scalar.Fqn
|
|
1741
|
-
* model.element({
|
|
1742
|
-
* id: 'dashboard',
|
|
1743
|
-
* })
|
|
1744
|
-
*/
|
|
1745
|
-
element(el) {
|
|
1746
|
-
if (el instanceof ElementModel) {
|
|
1747
|
-
return el;
|
|
1748
|
-
}
|
|
1749
|
-
const id = getId(el);
|
|
1750
|
-
return nonNullable(this._elements.get(id), `Element ${id} not found`);
|
|
1751
|
-
}
|
|
1752
|
-
/**
|
|
1753
|
-
* Returns the element with the given FQN.
|
|
1754
|
-
*
|
|
1755
|
-
* @returns Element if found, null otherwise
|
|
1756
|
-
* @note Method is not type-safe as {@link element}
|
|
1757
|
-
*
|
|
1758
|
-
* @example
|
|
1759
|
-
* model.findElement('cloud.frontend')
|
|
1760
|
-
*/
|
|
1761
|
-
findElement(el) {
|
|
1762
|
-
return this._elements.get(getId(el)) ?? null;
|
|
1763
|
-
}
|
|
1764
|
-
/**
|
|
1765
|
-
* Returns the root elements of the model.
|
|
1766
|
-
*/
|
|
1767
|
-
roots() {
|
|
1768
|
-
return this._rootElements.values();
|
|
1769
|
-
}
|
|
1770
|
-
/**
|
|
1771
|
-
* Returns all elements in the model.
|
|
1772
|
-
*/
|
|
1773
|
-
elements() {
|
|
1774
|
-
return this._elements.values();
|
|
1775
|
-
}
|
|
1776
|
-
/**
|
|
1777
|
-
* Returns all relationships in the model.
|
|
1778
|
-
*/
|
|
1779
|
-
relationships() {
|
|
1780
|
-
return this._relations.values();
|
|
1781
|
-
}
|
|
1782
|
-
relationship(rel, type) {
|
|
1783
|
-
if (type === "deployment") {
|
|
1784
|
-
return this.deployment.relationship(rel);
|
|
1785
|
-
}
|
|
1786
|
-
const id = getId(rel);
|
|
1787
|
-
let model = this._relations.get(id) ?? null;
|
|
1788
|
-
if (model || type === "model") {
|
|
1789
|
-
return nonNullable(model, `Model relation ${id} not found`);
|
|
1790
|
-
}
|
|
1791
|
-
return nonNullable(this.deployment.findRelationship(id), `No model/deployment relation ${id} not found`);
|
|
1792
|
-
}
|
|
1793
|
-
findRelationship(id, type) {
|
|
1794
|
-
if (type === "deployment") {
|
|
1795
|
-
return this.deployment.findRelationship(id);
|
|
1796
|
-
}
|
|
1797
|
-
let model = this._relations.get(getId(id)) ?? null;
|
|
1798
|
-
if (model || type === "model") {
|
|
1799
|
-
return model;
|
|
1800
|
-
}
|
|
1801
|
-
return this.deployment.findRelationship(id);
|
|
1802
|
-
}
|
|
1803
|
-
/**
|
|
1804
|
-
* Returns all views in the model.
|
|
1805
|
-
*/
|
|
1806
|
-
views() {
|
|
1807
|
-
return this._views.values();
|
|
1808
|
-
}
|
|
1809
|
-
/**
|
|
1810
|
-
* Returns a specific view by its ID.
|
|
1811
|
-
* @note Method is type-safe for typed model
|
|
1812
|
-
* @throws Error if view is not found\
|
|
1813
|
-
* Use {@link findView} if you don't want to throw an error
|
|
1814
|
-
*
|
|
1815
|
-
* @example
|
|
1816
|
-
* model.view('index')
|
|
1817
|
-
* // or object with id property of scalar.ViewId
|
|
1818
|
-
* model.view({
|
|
1819
|
-
* id: 'index',
|
|
1820
|
-
* })
|
|
1821
|
-
*/
|
|
1822
|
-
view(viewId) {
|
|
1823
|
-
const id = getId(viewId);
|
|
1824
|
-
return nonNullable(this._views.get(id), `View ${id} not found`);
|
|
1825
|
-
}
|
|
1826
|
-
/**
|
|
1827
|
-
* Returns a specific view by its ID.
|
|
1828
|
-
* @note Method is not type-safe as {@link view}
|
|
1829
|
-
*
|
|
1830
|
-
* @example
|
|
1831
|
-
* model.findView('index')
|
|
1832
|
-
*/
|
|
1833
|
-
findView(viewId) {
|
|
1834
|
-
return this._views.get(viewId) ?? null;
|
|
1835
|
-
}
|
|
1836
|
-
/**
|
|
1837
|
-
* Returns manual layout snapshot for given view ID, if any.
|
|
1838
|
-
*/
|
|
1839
|
-
findManualLayout(viewId) {
|
|
1840
|
-
if ("manualLayouts" in this.$data) {
|
|
1841
|
-
const view = this.$data.manualLayouts?.[viewId];
|
|
1842
|
-
return view ?? null;
|
|
1843
|
-
}
|
|
1844
|
-
return null;
|
|
1845
|
-
}
|
|
1846
|
-
/**
|
|
1847
|
-
* Returns a view folder by its path.
|
|
1848
|
-
* Path is extracted from the view title, e.g. "Group 1/Group 2/View" -> "Group 1/Group 2"
|
|
1849
|
-
* @throws Error if view folder is not found.
|
|
1850
|
-
*/
|
|
1851
|
-
viewFolder(path) {
|
|
1852
|
-
return nonNullable(this._viewFolders.get(path), `View folder ${path} not found`);
|
|
1853
|
-
}
|
|
1854
|
-
/**
|
|
1855
|
-
* Root folder is a special one with an empty path and used only for internal purposes.
|
|
1856
|
-
* It is not visible to the user and should be used only to get top-level folders and views.
|
|
1857
|
-
*/
|
|
1858
|
-
get rootViewFolder() {
|
|
1859
|
-
return this._rootViewFolder;
|
|
1860
|
-
}
|
|
1861
|
-
/**
|
|
1862
|
-
* Whether the model has any view folders.
|
|
1863
|
-
*/
|
|
1864
|
-
get hasViewFolders() {
|
|
1865
|
-
return this._viewFolders.size > 1;
|
|
1866
|
-
}
|
|
1867
|
-
/**
|
|
1868
|
-
* Returns all children of a view folder.
|
|
1869
|
-
* Path is extracted from the view title, e.g. "Group 1/Group 2/View" -> "Group 1/Group 2"
|
|
1870
|
-
*
|
|
1871
|
-
* @throws Error if view folder is not found.
|
|
1872
|
-
*/
|
|
1873
|
-
viewFolderItems(path) {
|
|
1874
|
-
invariant(this._viewFolders.has(path), `View folder ${path} not found`);
|
|
1875
|
-
return this._viewFolderItems.get(path);
|
|
1876
|
-
}
|
|
1877
|
-
/**
|
|
1878
|
-
* Returns the parent element of given element.
|
|
1879
|
-
* @see ancestors
|
|
1880
|
-
*/
|
|
1881
|
-
parent(element) {
|
|
1882
|
-
const id = getId(element);
|
|
1883
|
-
return this._parents.get(id) || null;
|
|
1884
|
-
}
|
|
1885
|
-
/**
|
|
1886
|
-
* Get all children of the element (only direct children),
|
|
1887
|
-
* @see descendants
|
|
1888
|
-
*/
|
|
1889
|
-
children(element) {
|
|
1890
|
-
const id = getId(element);
|
|
1891
|
-
return this._children.get(id);
|
|
1892
|
-
}
|
|
1893
|
-
/**
|
|
1894
|
-
* Get all sibling (i.e. same parent)
|
|
1895
|
-
*/
|
|
1896
|
-
*siblings(element) {
|
|
1897
|
-
const id = getId(element);
|
|
1898
|
-
const parent = this._parents.get(id);
|
|
1899
|
-
const siblings = parent ? this._children.get(parent.id).values() : this.roots();
|
|
1900
|
-
for (const sibling of siblings) {
|
|
1901
|
-
if (sibling.id !== id) {
|
|
1902
|
-
yield sibling;
|
|
1903
|
-
}
|
|
1904
|
-
}
|
|
1905
|
-
return;
|
|
1906
|
-
}
|
|
1907
|
-
/**
|
|
1908
|
-
* Get all ancestor elements (i.e. parent, parent’s parent, etc.)
|
|
1909
|
-
* (from closest to root)
|
|
1910
|
-
*/
|
|
1911
|
-
*ancestors(element) {
|
|
1912
|
-
let id = getId(element);
|
|
1913
|
-
let parent;
|
|
1914
|
-
while (parent = this._parents.get(id)) {
|
|
1915
|
-
yield parent;
|
|
1916
|
-
id = parent.id;
|
|
1917
|
-
}
|
|
1918
|
-
return;
|
|
1919
|
-
}
|
|
1920
|
-
/**
|
|
1921
|
-
* Get all descendant elements (i.e. children, children’s children, etc.)
|
|
1922
|
-
*/
|
|
1923
|
-
*descendants(element) {
|
|
1924
|
-
for (const child of this.children(element)) {
|
|
1925
|
-
yield child;
|
|
1926
|
-
yield* this.descendants(child.id);
|
|
1927
|
-
}
|
|
1928
|
-
return;
|
|
1929
|
-
}
|
|
1930
|
-
/**
|
|
1931
|
-
* Incoming relationships to the element and its descendants
|
|
1932
|
-
* @see incomers
|
|
1933
|
-
*/
|
|
1934
|
-
*incoming(element, filter = "all") {
|
|
1935
|
-
const id = getId(element);
|
|
1936
|
-
for (const rel of this._incoming.get(id)) {
|
|
1937
|
-
switch (true) {
|
|
1938
|
-
case filter === "all":
|
|
1939
|
-
case (filter === "direct" && rel.target.id === id):
|
|
1940
|
-
case (filter === "to-descendants" && rel.target.id !== id):
|
|
1941
|
-
yield rel;
|
|
1942
|
-
break;
|
|
1943
|
-
}
|
|
1944
|
-
}
|
|
1945
|
-
return;
|
|
1946
|
-
}
|
|
1947
|
-
/**
|
|
1948
|
-
* Outgoing relationships from the element and its descendants
|
|
1949
|
-
* @see outgoers
|
|
1950
|
-
*/
|
|
1951
|
-
*outgoing(element, filter = "all") {
|
|
1952
|
-
const id = getId(element);
|
|
1953
|
-
for (const rel of this._outgoing.get(id)) {
|
|
1954
|
-
switch (true) {
|
|
1955
|
-
case filter === "all":
|
|
1956
|
-
case (filter === "direct" && rel.source.id === id):
|
|
1957
|
-
case (filter === "from-descendants" && rel.source.id !== id):
|
|
1958
|
-
yield rel;
|
|
1959
|
-
break;
|
|
1960
|
-
}
|
|
1961
|
-
}
|
|
1962
|
-
return;
|
|
1963
|
-
}
|
|
1964
|
-
/**
|
|
1965
|
-
* Returns array of all tags used in the model, sorted naturally.\
|
|
1966
|
-
* Use {@link specification.tags} to get all defined tags
|
|
1967
|
-
*/
|
|
1968
|
-
get tags() {
|
|
1969
|
-
return memoizeProp(this, "tags", () => t$7([...this._allTags.keys()], compareNatural));
|
|
1970
|
-
}
|
|
1971
|
-
/**
|
|
1972
|
-
* Returns all tags used in the model, sorted by usage count (descending).
|
|
1973
|
-
*/
|
|
1974
|
-
get tagsSortedByUsage() {
|
|
1975
|
-
return memoizeProp(this, "tagsSortedByUsage", () => t$6(
|
|
1976
|
-
[...this._allTags.entries()],
|
|
1977
|
-
t$8(([tag, tagged]) => ({
|
|
1978
|
-
tag,
|
|
1979
|
-
count: tagged.size,
|
|
1980
|
-
tagged
|
|
1981
|
-
})),
|
|
1982
|
-
t$7((a, b) => compareNatural(a.tag, b.tag)),
|
|
1983
|
-
t$9(
|
|
1984
|
-
[e$4("count"), "desc"]
|
|
1985
|
-
)
|
|
1986
|
-
));
|
|
1987
|
-
}
|
|
1988
|
-
findByTag(tag, type) {
|
|
1989
|
-
return ifilter(this._allTags.get(tag), (el) => {
|
|
1990
|
-
if (type === "elements") {
|
|
1991
|
-
return el instanceof ElementModel;
|
|
1992
|
-
}
|
|
1993
|
-
if (type === "views") {
|
|
1994
|
-
return el instanceof LikeC4ViewModel;
|
|
1995
|
-
}
|
|
1996
|
-
if (type === "relationships") {
|
|
1997
|
-
return el instanceof RelationshipModel;
|
|
1998
|
-
}
|
|
1999
|
-
return true;
|
|
2000
|
-
});
|
|
2001
|
-
}
|
|
2002
|
-
/**
|
|
2003
|
-
* Returns all elements of the given kind.
|
|
2004
|
-
*/
|
|
2005
|
-
*elementsOfKind(kind) {
|
|
2006
|
-
for (const el of this._elements.values()) {
|
|
2007
|
-
if (el.kind === kind) {
|
|
2008
|
-
yield el;
|
|
2009
|
-
}
|
|
2010
|
-
}
|
|
2011
|
-
return;
|
|
2012
|
-
}
|
|
2013
|
-
/**
|
|
2014
|
-
* Returns all elements that match the given where operator.
|
|
2015
|
-
*
|
|
2016
|
-
* @example
|
|
2017
|
-
* ```ts
|
|
2018
|
-
* model.where({
|
|
2019
|
-
* and: [
|
|
2020
|
-
* { kind: 'component' },
|
|
2021
|
-
* {
|
|
2022
|
-
* or: [
|
|
2023
|
-
* { tag: 'old' },
|
|
2024
|
-
* { tag: { neq: 'new' } },
|
|
2025
|
-
* ],
|
|
2026
|
-
* },
|
|
2027
|
-
* ],
|
|
2028
|
-
* })
|
|
2029
|
-
* ```
|
|
2030
|
-
*/
|
|
2031
|
-
*elementsWhere(where) {
|
|
2032
|
-
const predicate = whereOperatorAsPredicate(where);
|
|
2033
|
-
for (const el of this._elements.values()) {
|
|
2034
|
-
if (predicate(el)) {
|
|
2035
|
-
yield el;
|
|
2036
|
-
}
|
|
2037
|
-
}
|
|
2038
|
-
return;
|
|
2039
|
-
}
|
|
2040
|
-
/**
|
|
2041
|
-
* Returns all **model** relationships that match the given where operator.
|
|
2042
|
-
*
|
|
2043
|
-
* @example
|
|
2044
|
-
* ```ts
|
|
2045
|
-
* model.relationshipsWhere({
|
|
2046
|
-
* and: [
|
|
2047
|
-
* { kind: 'uses' },
|
|
2048
|
-
* {
|
|
2049
|
-
* or: [
|
|
2050
|
-
* { tag: 'old' },
|
|
2051
|
-
* { tag: { neq: 'new' } },
|
|
2052
|
-
* ],
|
|
2053
|
-
* },
|
|
2054
|
-
* ],
|
|
2055
|
-
* })
|
|
2056
|
-
* ```
|
|
2057
|
-
*/
|
|
2058
|
-
*relationshipsWhere(where) {
|
|
2059
|
-
const predicate = whereOperatorAsPredicate(where);
|
|
2060
|
-
for (const rel of this._relations.values()) {
|
|
2061
|
-
if (predicate(rel)) {
|
|
2062
|
-
yield rel;
|
|
2063
|
-
}
|
|
2064
|
-
}
|
|
2065
|
-
return;
|
|
2066
|
-
}
|
|
2067
|
-
addElement(element) {
|
|
2068
|
-
if (this._elements.has(element.id)) {
|
|
2069
|
-
throw new Error(`Element ${element.id} already exists`);
|
|
2070
|
-
}
|
|
2071
|
-
const el = new ElementModel(this, Object.freeze(element));
|
|
2072
|
-
this._elements.set(el.id, el);
|
|
2073
|
-
const parentId = parentFqn(el.id);
|
|
2074
|
-
if (parentId) {
|
|
2075
|
-
invariant(this._elements.has(parentId), `Parent ${parentId} of ${el.id} not found`);
|
|
2076
|
-
this._parents.set(el.id, this.element(parentId));
|
|
2077
|
-
this._children.get(parentId).add(el);
|
|
2078
|
-
} else {
|
|
2079
|
-
this._rootElements.add(el);
|
|
2080
|
-
}
|
|
2081
|
-
return el;
|
|
2082
|
-
}
|
|
2083
|
-
addImportedElement(projectId, element) {
|
|
2084
|
-
invariant(!isGlobalFqn(element.id), `Imported element already has global FQN`);
|
|
2085
|
-
const id = GlobalFqn(projectId, element.id);
|
|
2086
|
-
if (this._elements.has(id)) {
|
|
2087
|
-
throw new Error(`Element ${id} already exists`);
|
|
2088
|
-
}
|
|
2089
|
-
const el = new ElementModel(
|
|
2090
|
-
this,
|
|
2091
|
-
Object.freeze({
|
|
2092
|
-
...element,
|
|
2093
|
-
id
|
|
2094
|
-
})
|
|
2095
|
-
);
|
|
2096
|
-
this._elements.set(el.id, el);
|
|
2097
|
-
let parentId = parentFqn(el.id);
|
|
2098
|
-
while (parentId) {
|
|
2099
|
-
if (parentId.includes(".") && this._elements.has(parentId)) {
|
|
2100
|
-
this._parents.set(el.id, this.element(parentId));
|
|
2101
|
-
this._children.get(parentId).add(el);
|
|
2102
|
-
return el;
|
|
2103
|
-
}
|
|
2104
|
-
parentId = parentFqn(parentId);
|
|
2105
|
-
}
|
|
2106
|
-
this._rootElements.add(el);
|
|
2107
|
-
return el;
|
|
2108
|
-
}
|
|
2109
|
-
addRelation(relation) {
|
|
2110
|
-
if (this._relations.has(relation.id)) {
|
|
2111
|
-
throw new Error(`Relation ${relation.id} already exists`);
|
|
2112
|
-
}
|
|
2113
|
-
const rel = new RelationshipModel(
|
|
2114
|
-
this,
|
|
2115
|
-
Object.freeze(relation)
|
|
2116
|
-
);
|
|
2117
|
-
const { source, target } = rel;
|
|
2118
|
-
this._relations.set(rel.id, rel);
|
|
2119
|
-
this._incoming.get(target.id).add(rel);
|
|
2120
|
-
this._outgoing.get(source.id).add(rel);
|
|
2121
|
-
const relParent = commonAncestor(source.id, target.id);
|
|
2122
|
-
if (relParent) {
|
|
2123
|
-
for (const ancestor of [relParent, ...ancestorsFqn(relParent)]) {
|
|
2124
|
-
this._internal.get(ancestor).add(rel);
|
|
2125
|
-
}
|
|
2126
|
-
}
|
|
2127
|
-
for (const sourceAncestor of ancestorsFqn(source.id)) {
|
|
2128
|
-
if (sourceAncestor === relParent) {
|
|
2129
|
-
break;
|
|
2130
|
-
}
|
|
2131
|
-
this._outgoing.get(sourceAncestor).add(rel);
|
|
2132
|
-
}
|
|
2133
|
-
for (const targetAncestor of ancestorsFqn(target.id)) {
|
|
2134
|
-
if (targetAncestor === relParent) {
|
|
2135
|
-
break;
|
|
2136
|
-
}
|
|
2137
|
-
this._incoming.get(targetAncestor).add(rel);
|
|
2138
|
-
}
|
|
2139
|
-
return rel;
|
|
2140
|
-
}
|
|
2141
|
-
}
|
|
2142
|
-
((LikeC4Model2) => {
|
|
2143
|
-
LikeC4Model2.EMPTY = LikeC4Model2.create({
|
|
2144
|
-
_stage: "computed",
|
|
2145
|
-
projectId: "default",
|
|
2146
|
-
project: { id: "default" },
|
|
2147
|
-
specification: {
|
|
2148
|
-
elements: {},
|
|
2149
|
-
relationships: {},
|
|
2150
|
-
deployments: {},
|
|
2151
|
-
tags: {}
|
|
2152
|
-
},
|
|
2153
|
-
globals: {
|
|
2154
|
-
predicates: {},
|
|
2155
|
-
dynamicPredicates: {},
|
|
2156
|
-
styles: {}
|
|
2157
|
-
},
|
|
2158
|
-
deployments: {
|
|
2159
|
-
elements: {},
|
|
2160
|
-
relations: {}
|
|
2161
|
-
},
|
|
2162
|
-
elements: {},
|
|
2163
|
-
relations: {},
|
|
2164
|
-
views: {},
|
|
2165
|
-
imports: {}
|
|
2166
|
-
});
|
|
2167
|
-
})(LikeC4Model || (LikeC4Model = {}));
|
|
2168
|
-
|
|
2169
|
-
function isDeploymentNodeModel(model) {
|
|
2170
|
-
return model instanceof DeploymentNodeModel;
|
|
2171
|
-
}
|
|
2172
|
-
function isDeployedInstanceModel(model) {
|
|
2173
|
-
return model instanceof DeployedInstanceModel;
|
|
2174
|
-
}
|
|
2175
|
-
function isDeploymentElementModel(model) {
|
|
2176
|
-
return isDeploymentNodeModel(model) || isDeployedInstanceModel(model);
|
|
2177
|
-
}
|
|
2178
|
-
function isNestedElementOfDeployedInstanceModel(model) {
|
|
2179
|
-
return model instanceof NestedElementOfDeployedInstanceModel;
|
|
2180
|
-
}
|
|
2181
|
-
function isDeploymentRelationModel(x) {
|
|
2182
|
-
return x instanceof DeploymentRelationModel;
|
|
2183
|
-
}
|
|
2184
|
-
function isRelationModel(x) {
|
|
2185
|
-
return x instanceof RelationshipModel;
|
|
2186
|
-
}
|
|
2187
|
-
function isElementModel(element) {
|
|
2188
|
-
return element instanceof ElementModel;
|
|
2189
|
-
}
|
|
2190
|
-
function isLikeC4ViewModel(view) {
|
|
2191
|
-
return view instanceof LikeC4ViewModel;
|
|
2192
|
-
}
|
|
2193
|
-
function isNodeModel(node) {
|
|
2194
|
-
return node instanceof NodeModel;
|
|
2195
|
-
}
|
|
2196
|
-
function isEdgeModel(edge) {
|
|
2197
|
-
return edge instanceof EdgeModel;
|
|
2198
|
-
}
|
|
2199
|
-
|
|
2200
|
-
export { ElementModel as E, LikeC4Model as L, NodeModel as N, RelationshipModel as R, VIEW_FOLDERS_SEPARATOR as V, t$1 as a, isNestedElementOfDeployedInstanceModel as b, isDeployedInstanceModel as c, isDeploymentNodeModel as d, isDeploymentElementModel as e, LikeC4DeploymentModel as f, EdgeModel as g, LikeC4ViewModel as h, isElementModel as i, LikeC4ViewsFolder as j, extractViewTitleFromPath as k, getViewFolderPath as l, isDeploymentRelationModel as m, normalizeViewPath as n, isRelationModel as o, isLikeC4ViewModel as p, isNodeModel as q, isEdgeModel as r, t };
|