@likec4/core 1.18.0 → 1.19.1
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/builder/index.d.mts +502 -0
- package/dist/builder/index.d.ts +502 -0
- package/dist/builder/index.js +871 -0
- package/dist/compute-view/index.d.mts +9 -78
- package/dist/compute-view/index.d.ts +9 -78
- package/dist/compute-view/index.js +78 -0
- package/dist/index.d.mts +9 -730
- package/dist/index.d.ts +9 -730
- package/dist/{index.mjs → index.js} +7 -784
- package/dist/model/index.d.mts +218 -606
- package/dist/model/index.d.ts +218 -606
- package/dist/model/index.js +1 -0
- package/dist/shared/chunk-RAAYCPUM.M-JWF7SS.js +21 -0
- package/dist/shared/core.BIfgabEw.d.ts +719 -0
- package/dist/shared/core.BZ9Msq7w.d.mts +719 -0
- package/dist/shared/{core.DMYY7C8s.d.mts → core.C05RDLhi.d.ts} +179 -230
- package/dist/shared/{core.DMYY7C8s.d.ts → core.CmYMqgha.d.mts} +179 -230
- package/dist/shared/core.D6-fWbM3.d.ts +127 -0
- package/dist/shared/core.D74xkKkG.d.mts +148 -0
- package/dist/shared/core.D74xkKkG.d.ts +148 -0
- package/dist/shared/core.sLaWHBjR.d.mts +127 -0
- package/dist/shared/index.DafVOuVd.js +7443 -0
- package/dist/shared/object_hash.CE_oF3I3.js +1222 -0
- package/dist/shared/view.CyZrG8BJ.js +354 -0
- package/dist/types/index.d.mts +4 -2
- package/dist/types/index.d.ts +4 -2
- package/dist/types/index.js +20 -0
- package/dist/utils/index.d.mts +419 -0
- package/dist/utils/index.d.ts +419 -0
- package/dist/utils/index.js +347 -0
- package/package.json +47 -33
- package/src/builder/Builder-style1.test-d.ts +190 -0
- package/src/builder/Builder-style2.test-d.ts +175 -0
- package/src/builder/Builder.deployment.ts +25 -20
- package/src/builder/Builder.deploymentModel.ts +80 -55
- package/src/builder/Builder.element.ts +2 -0
- package/src/builder/Builder.model.ts +57 -43
- package/src/builder/Builder.ts +412 -431
- package/src/builder/Builder.view-common.ts +234 -0
- package/src/builder/Builder.view-deployment.ts +135 -0
- package/src/builder/Builder.view-element.ts +189 -0
- package/src/builder/Builder.views.ts +109 -33
- package/src/builder/Builder.with.ts +475 -0
- package/src/builder/__snapshots__/Builder-style1.spec.ts.snap +374 -0
- package/src/builder/__snapshots__/Builder-style2.spec.ts.snap +183 -0
- package/src/builder/__snapshots__/Builder.spec.ts.snap +374 -0
- package/src/builder/_types.ts +28 -5
- package/src/builder/index.ts +10 -4
- package/src/colors/index.ts +2 -2
- package/src/compute-view/compute-view.ts +45 -54
- package/src/compute-view/deployment-view/__test__/TestHelper.ts +236 -0
- package/src/compute-view/deployment-view/__test__/fixture.ts +50 -62
- package/src/compute-view/deployment-view/_types.ts +26 -18
- package/src/compute-view/deployment-view/clean-connections.ts +173 -164
- package/src/compute-view/deployment-view/compute.ts +51 -37
- package/src/compute-view/deployment-view/memory/index.ts +9 -0
- package/src/compute-view/deployment-view/memory/memory.ts +97 -0
- package/src/compute-view/deployment-view/predicates/elements.ts +144 -0
- package/src/compute-view/deployment-view/predicates/relation-direct.ts +183 -76
- package/src/compute-view/deployment-view/predicates/relation-in-out.ts +38 -20
- package/src/compute-view/deployment-view/predicates/relation-incoming.ts +72 -23
- package/src/compute-view/deployment-view/predicates/relation-outgoing.ts +61 -22
- package/src/compute-view/deployment-view/predicates/wildcard.ts +15 -15
- package/src/compute-view/deployment-view/stages/stage-exclude.ts +5 -0
- package/src/compute-view/deployment-view/stages/stage-final.ts +245 -0
- package/src/compute-view/deployment-view/stages/stage-include.ts +95 -0
- package/src/compute-view/deployment-view/utils.ts +95 -31
- package/src/compute-view/dynamic-view/__test__/fixture.ts +4 -4
- package/src/compute-view/dynamic-view/compute.ts +59 -45
- package/src/compute-view/element-view/__test__/TestHelper.ts +258 -0
- package/src/compute-view/element-view/__test__/__snapshots__/legacy.spec.ts.snap +64 -62
- package/src/compute-view/element-view/__test__/fixture.ts +147 -117
- package/src/compute-view/element-view/_types.ts +48 -0
- package/src/compute-view/element-view/clean-connections.ts +113 -0
- package/src/compute-view/element-view/compute.ts +264 -683
- package/src/compute-view/element-view/memory/NodeGroup.ts +31 -0
- package/src/compute-view/element-view/memory/index.ts +10 -0
- package/src/compute-view/element-view/memory/memory.ts +177 -0
- package/src/compute-view/element-view/memory/stage-exclude.ts +59 -0
- package/src/compute-view/element-view/memory/stage-final.ts +129 -0
- package/src/compute-view/element-view/memory/stage-include.ts +128 -0
- package/src/compute-view/element-view/predicates/_utils.ts +86 -0
- package/src/compute-view/element-view/predicates/element-expand.ts +33 -0
- package/src/compute-view/element-view/predicates/element-kind-tag.ts +26 -0
- package/src/compute-view/element-view/predicates/element-ref.ts +37 -0
- package/src/compute-view/element-view/predicates/relation-direct.ts +309 -0
- package/src/compute-view/element-view/predicates/relation-in-out.ts +66 -0
- package/src/compute-view/element-view/predicates/relation-in.ts +118 -0
- package/src/compute-view/element-view/predicates/relation-out.ts +116 -0
- package/src/compute-view/element-view/predicates/wildcard.ts +88 -0
- package/src/compute-view/element-view/utils.ts +59 -0
- package/src/compute-view/index.ts +3 -4
- package/src/compute-view/memory/AbstractMemory.ts +45 -0
- package/src/compute-view/memory/AbstractStageExclude.ts +216 -0
- package/src/compute-view/memory/AbstractStageInclude.ts +200 -0
- package/src/compute-view/memory/_types.ts +147 -0
- package/src/compute-view/memory/index.ts +5 -0
- package/src/compute-view/memory/ops.ts +43 -0
- package/src/compute-view/utils/buildComputedNodes.ts +4 -4
- package/src/compute-view/utils/elementExpressionToPredicate.ts +1 -3
- package/src/compute-view/utils/link-nodes-with-edges.ts +3 -7
- package/src/compute-view/utils/{derive-edge-props-from-relationships.ts → merge-props-from-relationships.ts} +54 -23
- package/src/compute-view/utils/relationExpressionToPredicates.ts +2 -2
- package/src/compute-view/utils/resolve-extended-views.ts +44 -40
- package/src/compute-view/utils/resolve-global-rules.ts +6 -6
- package/src/compute-view/utils/topological-sort.ts +167 -71
- package/src/compute-view/utils/with-readable-edges.ts +14 -9
- package/src/index.ts +22 -16
- package/src/model/DeploymentElementModel.ts +150 -85
- package/src/model/DeploymentModel.ts +27 -21
- package/src/model/ElementModel.ts +51 -25
- package/src/model/LikeC4Model.test-d.ts +4 -3
- package/src/model/LikeC4Model.ts +98 -42
- package/src/model/RelationModel.ts +5 -4
- package/src/model/__test__/fixture.ts +5 -9
- package/src/model/connection/Connection.ts +72 -0
- package/src/model/connection/deployment/DeploymentConnectionModel.ts +178 -0
- package/src/model/connection/{deployment.ts → deployment/find.ts} +14 -18
- package/src/model/connection/deployment/index.ts +8 -0
- package/src/model/connection/index.ts +22 -0
- package/src/model/connection/model/ConnectionModel.ts +157 -0
- package/src/model/connection/{model.ts → model/find.ts} +43 -49
- package/src/model/connection/model/index.ts +8 -0
- package/src/model/connection/ops.ts +274 -0
- package/src/model/guards.ts +9 -0
- package/src/model/index.ts +45 -3
- package/src/model/types.ts +35 -18
- package/src/model/view/EdgeModel.ts +6 -6
- package/src/model/view/LikeC4ViewModel.ts +14 -8
- package/src/model/view/NodeModel.ts +6 -5
- package/src/types/_common.ts +53 -1
- package/src/types/deployments.ts +88 -156
- package/src/types/element.ts +2 -2
- package/src/types/expression-v2.ts +172 -0
- package/src/types/expression.ts +9 -5
- package/src/types/global.ts +3 -3
- package/src/types/index.ts +220 -13
- package/src/types/model.ts +28 -24
- package/src/types/operators.ts +1 -1
- package/src/types/relation.ts +14 -10
- package/src/types/theme.ts +0 -1
- package/src/types/view.ts +20 -18
- package/src/utils/const.ts +1 -0
- package/src/utils/fqn.ts +140 -49
- package/src/utils/getOrCreate.ts +1 -1
- package/src/utils/index.ts +75 -9
- package/src/utils/iterable/_types.ts +5 -0
- package/src/utils/iterable/filter.ts +55 -0
- package/src/utils/iterable/find.ts +36 -0
- package/src/utils/iterable/flat.ts +17 -0
- package/src/utils/iterable/index.ts +13 -0
- package/src/utils/iterable/map.ts +40 -0
- package/src/utils/iterable/reduce.ts +24 -0
- package/src/utils/iterable/some.ts +36 -0
- package/src/utils/iterable/to.ts +19 -0
- package/src/utils/iterable/unique.ts +29 -0
- package/src/utils/relations.ts +3 -64
- package/src/utils/set.ts +11 -9
- package/src/utils/string-hash.ts +1 -1
- package/dist/compute-view/index.mjs +0 -79
- package/dist/model/index.mjs +0 -2
- package/dist/shared/core.BBgiAAv1.mjs +0 -144
- package/dist/shared/core.BPyJvuMU.mjs +0 -2420
- package/dist/shared/core.BQ4AbfvB.mjs +0 -9228
- package/dist/shared/core.k12s_keD.mjs +0 -203
- package/dist/types/index.mjs +0 -21
- package/src/builder/Builder.test-d.ts +0 -124
- package/src/builder/Builder.view.ts +0 -543
- package/src/compute-view/LikeC4ModelGraph.ts +0 -330
- package/src/compute-view/deployment-view/Memory.ts +0 -108
- package/src/compute-view/deployment-view/Stage.ts +0 -145
- package/src/compute-view/deployment-view/predicates/deployment-ref.ts +0 -146
- package/src/compute-view/element-view/predicates.ts +0 -517
- package/src/compute-view/utils/sortNodes.ts +0 -101
- package/src/model/connection/ConnectionModel.ts +0 -94
- package/src/model/connection/DeploymentConnectionModel.ts +0 -99
|
@@ -1,36 +1,49 @@
|
|
|
1
1
|
import DefaultMap from 'mnemonist/default-map'
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import {
|
|
3
|
+
dropWhile,
|
|
4
|
+
filter,
|
|
5
|
+
flatMap,
|
|
6
|
+
forEach,
|
|
7
|
+
groupBy,
|
|
8
|
+
hasAtLeast,
|
|
9
|
+
map,
|
|
10
|
+
pipe,
|
|
11
|
+
piped,
|
|
12
|
+
prop,
|
|
13
|
+
reduce,
|
|
14
|
+
values,
|
|
15
|
+
} from 'remeda'
|
|
16
|
+
import { invariant } from '../../errors'
|
|
17
|
+
import {
|
|
18
|
+
DeploymentConnectionModel,
|
|
19
|
+
differenceConnections,
|
|
20
|
+
isNestedConnection,
|
|
21
|
+
mergeConnections,
|
|
22
|
+
sortConnectionsByBoundaryHierarchy,
|
|
23
|
+
} from '../../model/connection'
|
|
24
|
+
import { findConnection } from '../../model/connection/deployment'
|
|
5
25
|
import { RelationshipsAccum } from '../../model/DeploymentElementModel'
|
|
6
26
|
import type { RelationshipModel } from '../../model/RelationModel'
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import type { Connections, Elem } from './_types'
|
|
11
|
-
import { type Patch } from './Memory'
|
|
27
|
+
import { imap, toArray } from '../../utils/iterable'
|
|
28
|
+
import { intersection, union } from '../../utils/set'
|
|
29
|
+
import type { Connection, Connections } from './_types'
|
|
12
30
|
|
|
13
31
|
const filterEmptyConnection = filter((c: DeploymentConnectionModel<any>) => c.nonEmpty())
|
|
14
32
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const connectionBoundary = (conn: DeploymentConnectionModel<any>) => conn.boundary?.id ? `.${conn.boundary.id}` : ''
|
|
18
|
-
|
|
19
|
-
function cleanCrossBoundary<M extends AnyAux>(connections: Connections<M>): Connections<M> {
|
|
20
|
-
// Keep only connections between leafs
|
|
33
|
+
type MapOfExcludesFromConnection = DefaultMap<DeploymentConnectionModel, Set<RelationshipModel>>
|
|
34
|
+
function findCrossBoundarySameSourceOrTarget(connections: Connections): MapOfExcludesFromConnection {
|
|
21
35
|
// Also find connections based on same relation
|
|
22
|
-
const groupedByRelation = new DefaultMap<RelationshipModel
|
|
36
|
+
const groupedByRelation = new DefaultMap<RelationshipModel, Array<DeploymentConnectionModel>>(() => [])
|
|
23
37
|
for (const conn of connections) {
|
|
24
38
|
for (const relation of conn.relations.model) {
|
|
25
39
|
groupedByRelation.get(relation).push(conn)
|
|
26
40
|
}
|
|
27
41
|
}
|
|
28
42
|
|
|
29
|
-
//
|
|
30
|
-
// So we create new instances without excluded relations
|
|
43
|
+
// Aggreate excluded relations for each connection
|
|
31
44
|
const excludedRelations = new DefaultMap<
|
|
32
|
-
DeploymentConnectionModel
|
|
33
|
-
Set<RelationshipModel
|
|
45
|
+
DeploymentConnectionModel,
|
|
46
|
+
Set<RelationshipModel>
|
|
34
47
|
>(() => new Set())
|
|
35
48
|
|
|
36
49
|
// In each group, find connected to same leaf
|
|
@@ -44,186 +57,182 @@ function cleanCrossBoundary<M extends AnyAux>(connections: Connections<M>): Conn
|
|
|
44
57
|
sameRelationGroup,
|
|
45
58
|
flatMap(conn => [
|
|
46
59
|
{ group: `$source-${conn.source.id}`, conn },
|
|
47
|
-
{ group: `$target-${conn.target.id}`, conn }
|
|
60
|
+
{ group: `$target-${conn.target.id}`, conn },
|
|
48
61
|
]),
|
|
49
62
|
groupBy(prop('group')),
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
connections,
|
|
63
|
+
values(),
|
|
64
|
+
filter(hasAtLeast(2)),
|
|
65
|
+
forEach(
|
|
66
|
+
// In each group, sort by hierarchy, first are deepest
|
|
67
|
+
piped(
|
|
56
68
|
map(prop('conn')),
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
//
|
|
69
|
+
sortConnectionsByBoundaryHierarchy('desc'),
|
|
70
|
+
// Drop first, as it is the deepest
|
|
71
|
+
// Drop if boundary is same as previous
|
|
60
72
|
dropWhile((conn, i, all) => i === 0 || conn.boundary === all[i - 1]!.boundary),
|
|
61
|
-
//
|
|
73
|
+
// Drop relations from boundaries above
|
|
62
74
|
forEach((conn) => {
|
|
63
75
|
excludedRelations.get(conn).add(relation)
|
|
64
|
-
})
|
|
65
|
-
)
|
|
66
|
-
|
|
76
|
+
}),
|
|
77
|
+
),
|
|
78
|
+
),
|
|
67
79
|
)
|
|
68
80
|
}
|
|
69
81
|
|
|
82
|
+
return excludedRelations
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Identifies and processes cross-boundary connections in a deployment diagram.
|
|
87
|
+
* This function analyzes connections between nodes and removes redundant relations
|
|
88
|
+
* when multiple connections exist between the same elements across different boundaries.
|
|
89
|
+
*
|
|
90
|
+
* The function performs the following:
|
|
91
|
+
* 1. Groups connections by their relationship type
|
|
92
|
+
* 2. For each relationship group, identifies connections that:
|
|
93
|
+
* - Share the same source
|
|
94
|
+
* - Share the same target
|
|
95
|
+
* 3. When multiple connections are found in the same group, keeps only the most specific
|
|
96
|
+
* (deepest in hierarchy) connection and removes the relationship from others
|
|
97
|
+
*
|
|
98
|
+
* @param connections - The input collection of deployment connections to analyze
|
|
99
|
+
* @returns connections with redundant cross-boundary relationships
|
|
100
|
+
*/
|
|
101
|
+
export function findCrossBoundaryConnections(connections: Connections): Array<Connection> {
|
|
102
|
+
// Ensure connections are merged
|
|
103
|
+
connections = mergeConnections(connections)
|
|
104
|
+
|
|
105
|
+
const excludedRelations = findCrossBoundarySameSourceOrTarget(connections)
|
|
106
|
+
|
|
107
|
+
for (const c of connections) {
|
|
108
|
+
const { source, target } = c
|
|
109
|
+
const connectionModelRelations = c.relations.model
|
|
110
|
+
// Connections between node and instance
|
|
111
|
+
// Exclude internal relations of the node (otherwise they are cross-boundary)
|
|
112
|
+
if (source.isDeploymentNode() !== target.isDeploymentNode()) {
|
|
113
|
+
const node = source.isDeploymentNode() ? source : c.target
|
|
114
|
+
invariant(node.isDeploymentNode())
|
|
115
|
+
const nodeInternals = node.internalModelRelationships()
|
|
116
|
+
const toExclude = intersection(
|
|
117
|
+
connectionModelRelations,
|
|
118
|
+
nodeInternals,
|
|
119
|
+
)
|
|
120
|
+
for (const relation of toExclude) {
|
|
121
|
+
excludedRelations.get(c).add(relation)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
continue
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Connection between nodes
|
|
128
|
+
if (source.isDeploymentNode() && target.isDeploymentNode()) {
|
|
129
|
+
const toExclude = union(
|
|
130
|
+
// Exclude node internals (otherwise thay are cross-boundary)
|
|
131
|
+
intersection(
|
|
132
|
+
c.relations.model,
|
|
133
|
+
source.internalModelRelationships(),
|
|
134
|
+
),
|
|
135
|
+
intersection(
|
|
136
|
+
c.relations.model,
|
|
137
|
+
target.internalModelRelationships(),
|
|
138
|
+
),
|
|
139
|
+
)
|
|
140
|
+
for (const relation of toExclude) {
|
|
141
|
+
excludedRelations.get(c).add(relation)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
70
146
|
return pipe(
|
|
147
|
+
excludedRelations.entries(),
|
|
148
|
+
imap(([c, excluded]) =>
|
|
149
|
+
c.update({
|
|
150
|
+
model: excluded,
|
|
151
|
+
deployment: null,
|
|
152
|
+
})
|
|
153
|
+
),
|
|
154
|
+
toArray(),
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Cleans connections that cross boundaries by removing overlapping parts.
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* const connections = getConnections();
|
|
163
|
+
* const cleanedConnections = cleanCrossBoundary(connections);
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
export function cleanCrossBoundary(connections: Connections): Array<Connection> {
|
|
167
|
+
return differenceConnections(
|
|
71
168
|
connections,
|
|
72
|
-
|
|
73
|
-
const excluded = excludedRelations.get(conn)
|
|
74
|
-
if (excluded && excluded.size > 0) {
|
|
75
|
-
const updated = new DeploymentConnectionModel(
|
|
76
|
-
conn.source,
|
|
77
|
-
conn.target,
|
|
78
|
-
new RelationshipsAccum(
|
|
79
|
-
difference(conn.relations.model, excluded),
|
|
80
|
-
conn.relations.deployment
|
|
81
|
-
)
|
|
82
|
-
)
|
|
83
|
-
return updated
|
|
84
|
-
}
|
|
85
|
-
return conn
|
|
86
|
-
}),
|
|
87
|
-
filterEmptyConnection
|
|
169
|
+
findCrossBoundaryConnections(connections),
|
|
88
170
|
)
|
|
89
171
|
}
|
|
90
172
|
|
|
91
173
|
/**
|
|
92
|
-
*
|
|
93
|
-
*
|
|
174
|
+
* Identifies and returns redundant connections in a deployment view.
|
|
175
|
+
* A connection is considered redundant if its relationships are already implied
|
|
176
|
+
* through other connections
|
|
94
177
|
*
|
|
95
|
-
* @returns
|
|
96
|
-
* Connection may be empty if all relationships are redundant, in this case it should be removed
|
|
178
|
+
* @returns Array of redundant connections that can be safely removed
|
|
97
179
|
*/
|
|
98
|
-
function
|
|
99
|
-
connections: Connections<M>
|
|
100
|
-
): Array<DeploymentConnectionModel<M>> {
|
|
180
|
+
export function findRedundantConnections(connections: Connections): Array<Connection> {
|
|
101
181
|
return pipe(
|
|
102
182
|
connections,
|
|
103
|
-
|
|
104
|
-
|
|
183
|
+
mergeConnections,
|
|
184
|
+
reduce((redundants, connection) => {
|
|
185
|
+
const { source, target, relations } = connection
|
|
105
186
|
|
|
106
187
|
// Connection between instances is always explicit
|
|
107
188
|
if (source.isInstance() && target.isInstance()) {
|
|
108
|
-
return
|
|
189
|
+
return redundants
|
|
109
190
|
}
|
|
110
191
|
|
|
111
|
-
let
|
|
112
|
-
let deploymentRelations = connection.relations.deployment
|
|
192
|
+
let redundantAccum = RelationshipsAccum.empty()
|
|
113
193
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|| isAncestor(target.id, c.target.id) && c.source.id === source.id
|
|
119
|
-
) {
|
|
120
|
-
modelRelations = difference(modelRelations, c.relations.model)
|
|
121
|
-
deploymentRelations = difference(deploymentRelations, c.relations.deployment)
|
|
194
|
+
if (source.isDeploymentNode() && target.isDeploymentNode()) {
|
|
195
|
+
const [reversed] = findConnection(target, source, 'directed')
|
|
196
|
+
if (reversed) {
|
|
197
|
+
redundantAccum = relations.intersect(reversed.relations)
|
|
122
198
|
}
|
|
123
199
|
}
|
|
124
200
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
target,
|
|
130
|
-
new RelationshipsAccum(
|
|
131
|
-
modelRelations,
|
|
132
|
-
deploymentRelations
|
|
201
|
+
for (const c of connections) {
|
|
202
|
+
if (isNestedConnection(c, connection)) {
|
|
203
|
+
redundantAccum = redundantAccum.union(
|
|
204
|
+
relations.intersect(c.relations),
|
|
133
205
|
)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (redundantAccum.nonEmpty) {
|
|
210
|
+
redundants.push(
|
|
211
|
+
new DeploymentConnectionModel(
|
|
212
|
+
source,
|
|
213
|
+
target,
|
|
214
|
+
redundantAccum,
|
|
215
|
+
),
|
|
134
216
|
)
|
|
135
217
|
}
|
|
136
218
|
|
|
137
|
-
return
|
|
138
|
-
}),
|
|
139
|
-
filterEmptyConnection
|
|
219
|
+
return redundants
|
|
220
|
+
}, [] as DeploymentConnectionModel[]),
|
|
140
221
|
)
|
|
141
222
|
}
|
|
223
|
+
|
|
142
224
|
/**
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
225
|
+
* Remove relationships from connection model, that are already included in the connections between descendants.
|
|
226
|
+
* In other words - if there is same connection down the hierarchy.
|
|
227
|
+
*
|
|
228
|
+
* @returns New connection without redundant relationships
|
|
229
|
+
* Connection may be empty if all relationships are redundant, in this case it should be removed
|
|
148
230
|
*/
|
|
149
|
-
export
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
for (const element of memory.elements) {
|
|
157
|
-
// Instance is always leaf
|
|
158
|
-
if (element.isInstance()) {
|
|
159
|
-
leafs.add(element)
|
|
160
|
-
continue
|
|
161
|
-
}
|
|
162
|
-
// Check if element is ancestor of any element in elements
|
|
163
|
-
let isLeaf = true
|
|
164
|
-
for (const el of memory.elements) {
|
|
165
|
-
if (isAncestor(element.id, el.id)) {
|
|
166
|
-
isLeaf = false
|
|
167
|
-
break
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
if (isLeaf) {
|
|
171
|
-
leafs.add(element)
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const connectedElements = new Set<Elem>()
|
|
176
|
-
|
|
177
|
-
const connections = pipe(
|
|
178
|
-
memory.connections,
|
|
179
|
-
mergeConnections,
|
|
180
|
-
// Keep connections
|
|
181
|
-
// - between leafs
|
|
182
|
-
// - has direct deployment relation
|
|
183
|
-
filter(c => {
|
|
184
|
-
return (leafs.has(c.source) && leafs.has(c.target)) || c.hasDirectDeploymentRelation()
|
|
185
|
-
}),
|
|
186
|
-
cleanCrossBoundary,
|
|
187
|
-
excludeRedundantRelationships,
|
|
188
|
-
forEach((c) => {
|
|
189
|
-
connectedElements.add(c.source)
|
|
190
|
-
connectedElements.add(c.target)
|
|
191
|
-
if (c.boundary) {
|
|
192
|
-
connectedElements.add(c.boundary)
|
|
193
|
-
}
|
|
194
|
-
})
|
|
231
|
+
export function cleanRedundantRelationships(
|
|
232
|
+
connections: Connections,
|
|
233
|
+
): Array<Connection> {
|
|
234
|
+
return differenceConnections(
|
|
235
|
+
connections,
|
|
236
|
+
findRedundantConnections(connections),
|
|
195
237
|
)
|
|
196
|
-
|
|
197
|
-
const isAncestorOfConnected = (el: Elem) => {
|
|
198
|
-
if (el.isInstance()) {
|
|
199
|
-
return false
|
|
200
|
-
}
|
|
201
|
-
for (const connected of connectedElements) {
|
|
202
|
-
if (isAncestor(el.id, connected.id)) {
|
|
203
|
-
return true
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
return false
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Update memory
|
|
210
|
-
const newMemory = memory.clone()
|
|
211
|
-
newMemory.connections = connections
|
|
212
|
-
|
|
213
|
-
// Iterate over elements (preserving order) and pick:
|
|
214
|
-
// - explicitly included
|
|
215
|
-
// - connected elements
|
|
216
|
-
// - ancestors of connected elements
|
|
217
|
-
const finalElements = new Set<Elem>()
|
|
218
|
-
for (const el of newMemory.finalElements) {
|
|
219
|
-
if (
|
|
220
|
-
newMemory.isExplicit(el)
|
|
221
|
-
|| connectedElements.has(el)
|
|
222
|
-
|| isAncestorOfConnected(el)
|
|
223
|
-
) {
|
|
224
|
-
finalElements.add(el)
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
newMemory.finalElements = finalElements
|
|
228
|
-
return newMemory
|
|
229
238
|
}
|
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
import { findLast } from 'remeda'
|
|
1
|
+
import { findLast, map } from 'remeda'
|
|
2
2
|
import { nonexhaustive } from '../../errors'
|
|
3
3
|
import { type LikeC4DeploymentModel, LikeC4Model } from '../../model'
|
|
4
4
|
import type { AnyAux } from '../../model/types'
|
|
5
5
|
import type { DeploymentViewRule } from '../../types'
|
|
6
6
|
import {
|
|
7
7
|
type ComputedDeploymentView,
|
|
8
|
-
DeploymentElementExpression,
|
|
9
|
-
DeploymentRelationExpression,
|
|
10
8
|
type DeploymentView,
|
|
9
|
+
FqnExpr,
|
|
11
10
|
isViewRuleAutoLayout,
|
|
12
|
-
isViewRulePredicate
|
|
11
|
+
isViewRulePredicate,
|
|
13
12
|
} from '../../types'
|
|
13
|
+
import { RelationExpr } from '../../types/expression-v2'
|
|
14
|
+
import { buildElementNotations } from '../utils/buildElementNotations'
|
|
14
15
|
import { linkNodesWithEdges } from '../utils/link-nodes-with-edges'
|
|
15
16
|
import { topologicalSort } from '../utils/topological-sort'
|
|
16
17
|
import { calcViewLayoutHash } from '../utils/view-hash'
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { DeploymentRefPredicate } from './predicates/deployment-ref'
|
|
18
|
+
import { Memory } from './memory'
|
|
19
|
+
import { DeploymentRefPredicate } from './predicates/elements'
|
|
20
20
|
import { DirectRelationPredicate } from './predicates/relation-direct'
|
|
21
21
|
import { InOutRelationPredicate } from './predicates/relation-in-out'
|
|
22
22
|
import { IncomingRelationPredicate } from './predicates/relation-incoming'
|
|
23
23
|
import { OutgoingRelationPredicate } from './predicates/relation-outgoing'
|
|
24
24
|
import { WildcardPredicate } from './predicates/wildcard'
|
|
25
|
-
import {
|
|
25
|
+
import { StageFinal } from './stages/stage-final'
|
|
26
26
|
import { applyDeploymentViewRuleStyles, buildNodes, toComputedEdges } from './utils'
|
|
27
27
|
|
|
28
|
-
function processPredicates<M extends AnyAux>(
|
|
28
|
+
export function processPredicates<M extends AnyAux>(
|
|
29
29
|
model: LikeC4DeploymentModel<M>,
|
|
30
|
-
rules: DeploymentViewRule[]
|
|
30
|
+
rules: DeploymentViewRule[],
|
|
31
31
|
) {
|
|
32
|
-
let memory =
|
|
33
|
-
let stage: Stage | null = null
|
|
32
|
+
let memory = Memory.empty()
|
|
34
33
|
|
|
35
34
|
for (const rule of rules) {
|
|
36
35
|
if (isViewRulePredicate(rule)) {
|
|
37
36
|
const op = 'include' in rule ? 'include' : 'exclude'
|
|
38
37
|
const exprs = rule.include ?? rule.exclude
|
|
39
38
|
for (const expr of exprs) {
|
|
40
|
-
stage =
|
|
39
|
+
let stage = op === 'include' ? memory.stageInclude(expr) : memory.stageExclude(expr)
|
|
41
40
|
const ctx = { model, stage, memory }
|
|
42
|
-
let patch: Patch | undefined
|
|
43
41
|
switch (true) {
|
|
44
|
-
case
|
|
45
|
-
|
|
42
|
+
case FqnExpr.isModelRef(expr):
|
|
43
|
+
// Ignore model refs in deployment view
|
|
46
44
|
break
|
|
47
|
-
case
|
|
48
|
-
|
|
45
|
+
case FqnExpr.isDeploymentRef(expr):
|
|
46
|
+
stage = DeploymentRefPredicate[op]({ ...ctx, expr } as any) ?? stage
|
|
49
47
|
break
|
|
50
|
-
case
|
|
51
|
-
|
|
48
|
+
case FqnExpr.isWildcard(expr):
|
|
49
|
+
stage = WildcardPredicate[op]({ ...ctx, expr } as any) ?? stage
|
|
52
50
|
break
|
|
53
|
-
case
|
|
54
|
-
|
|
51
|
+
case RelationExpr.isDirect(expr):
|
|
52
|
+
stage = DirectRelationPredicate[op]({ ...ctx, expr } as any) ?? stage
|
|
55
53
|
break
|
|
56
|
-
case
|
|
57
|
-
|
|
54
|
+
case RelationExpr.isInOut(expr):
|
|
55
|
+
stage = InOutRelationPredicate[op]({ ...ctx, expr } as any) ?? stage
|
|
58
56
|
break
|
|
59
|
-
case
|
|
60
|
-
|
|
57
|
+
case RelationExpr.isOutgoing(expr):
|
|
58
|
+
stage = OutgoingRelationPredicate[op]({ ...ctx, expr } as any) ?? stage
|
|
59
|
+
break
|
|
60
|
+
case RelationExpr.isIncoming(expr):
|
|
61
|
+
stage = IncomingRelationPredicate[op]({ ...ctx, expr } as any) ?? stage
|
|
61
62
|
break
|
|
62
63
|
default:
|
|
63
64
|
nonexhaustive(expr)
|
|
64
65
|
}
|
|
65
|
-
|
|
66
|
-
memory = patch(memory)
|
|
66
|
+
memory = stage.commit()
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
|
-
return
|
|
70
|
+
return StageFinal.for(memory).commit()
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
export function computeDeploymentView<M extends AnyAux>(
|
|
@@ -76,9 +76,9 @@ export function computeDeploymentView<M extends AnyAux>(
|
|
|
76
76
|
docUri: _docUri, // exclude docUri
|
|
77
77
|
rules, // exclude rules
|
|
78
78
|
...view
|
|
79
|
-
}: DeploymentView
|
|
79
|
+
}: DeploymentView,
|
|
80
80
|
): ComputedDeploymentView<M['ViewId']> {
|
|
81
|
-
|
|
81
|
+
let memory = processPredicates<M>(likec4model.deployment, rules)
|
|
82
82
|
|
|
83
83
|
const nodesMap = buildNodes(memory)
|
|
84
84
|
|
|
@@ -87,25 +87,39 @@ export function computeDeploymentView<M extends AnyAux>(
|
|
|
87
87
|
linkNodesWithEdges(nodesMap, computedEdges)
|
|
88
88
|
|
|
89
89
|
const sorted = topologicalSort({
|
|
90
|
-
nodes:
|
|
91
|
-
edges: computedEdges
|
|
90
|
+
nodes: nodesMap,
|
|
91
|
+
edges: computedEdges,
|
|
92
92
|
})
|
|
93
93
|
|
|
94
94
|
const nodes = applyDeploymentViewRuleStyles(
|
|
95
95
|
rules,
|
|
96
|
-
sorted.nodes
|
|
96
|
+
sorted.nodes,
|
|
97
97
|
)
|
|
98
98
|
|
|
99
99
|
const autoLayoutRule = findLast(rules, isViewRuleAutoLayout)
|
|
100
100
|
|
|
101
|
+
const elementNotations = buildElementNotations(nodes)
|
|
102
|
+
|
|
101
103
|
return calcViewLayoutHash({
|
|
102
104
|
...view,
|
|
103
105
|
autoLayout: {
|
|
104
106
|
direction: autoLayoutRule?.direction ?? 'TB',
|
|
105
107
|
...(autoLayoutRule?.nodeSep && { nodeSep: autoLayoutRule.nodeSep }),
|
|
106
|
-
...(autoLayoutRule?.rankSep && { rankSep: autoLayoutRule.rankSep })
|
|
108
|
+
...(autoLayoutRule?.rankSep && { rankSep: autoLayoutRule.rankSep }),
|
|
107
109
|
},
|
|
108
|
-
|
|
109
|
-
|
|
110
|
+
edges: sorted.edges,
|
|
111
|
+
nodes: map(nodes, n => {
|
|
112
|
+
// omit notation
|
|
113
|
+
delete n.notation
|
|
114
|
+
if (n.icon === 'none') {
|
|
115
|
+
delete n.icon
|
|
116
|
+
}
|
|
117
|
+
return n
|
|
118
|
+
}),
|
|
119
|
+
...(elementNotations.length > 0 && {
|
|
120
|
+
notation: {
|
|
121
|
+
elements: elementNotations,
|
|
122
|
+
},
|
|
123
|
+
}),
|
|
110
124
|
})
|
|
111
125
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { StageExclude } from '../stages/stage-exclude'
|
|
2
|
+
import type { StageInclude } from '../stages/stage-include'
|
|
3
|
+
|
|
4
|
+
export { StageExclude } from '../stages/stage-exclude'
|
|
5
|
+
export { StageInclude } from '../stages/stage-include'
|
|
6
|
+
export { Memory } from './memory'
|
|
7
|
+
export type { Ctx } from './memory'
|
|
8
|
+
|
|
9
|
+
export type Stage = StageInclude | StageExclude
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { isDeepEqual } from 'remeda'
|
|
2
|
+
import { type DeploymentConnectionModel, differenceConnections } from '../../../model/connection'
|
|
3
|
+
import type { DeploymentElementModel } from '../../../model/DeploymentElementModel'
|
|
4
|
+
import type { AnyAux } from '../../../model/types'
|
|
5
|
+
import type { ExpressionV2 } from '../../../types'
|
|
6
|
+
import { difference as differenceSet } from '../../../utils'
|
|
7
|
+
import { customInspectSymbol } from '../../../utils/const'
|
|
8
|
+
import { toArray } from '../../../utils/iterable'
|
|
9
|
+
import { AbstractMemory, type ComputeCtx, type StageExpression } from '../../memory'
|
|
10
|
+
import { StageExclude } from '../stages/stage-exclude'
|
|
11
|
+
import { StageInclude } from '../stages/stage-include'
|
|
12
|
+
|
|
13
|
+
export type Ctx = ComputeCtx<
|
|
14
|
+
DeploymentElementModel<AnyAux>,
|
|
15
|
+
DeploymentConnectionModel<AnyAux>,
|
|
16
|
+
Memory,
|
|
17
|
+
StageInclude,
|
|
18
|
+
StageExclude,
|
|
19
|
+
ExpressionV2
|
|
20
|
+
>
|
|
21
|
+
|
|
22
|
+
export class Memory extends AbstractMemory<Ctx> {
|
|
23
|
+
static empty(): Memory {
|
|
24
|
+
return new Memory({
|
|
25
|
+
elements: new Set(),
|
|
26
|
+
explicits: new Set(),
|
|
27
|
+
final: new Set(),
|
|
28
|
+
connections: [],
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
override stageInclude(expr: StageExpression<Ctx>): StageInclude {
|
|
33
|
+
return new StageInclude(this, expr)
|
|
34
|
+
}
|
|
35
|
+
override stageExclude(expr: StageExpression<Ctx>): StageExclude {
|
|
36
|
+
return new StageExclude(this, expr)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
override mutableState(): Ctx['MutableState'] {
|
|
40
|
+
return ({
|
|
41
|
+
elements: new Set(this.state.elements),
|
|
42
|
+
explicits: new Set(this.state.explicits),
|
|
43
|
+
final: new Set(this.state.final),
|
|
44
|
+
connections: [...this.state.connections],
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
override update(newstate: Partial<Ctx['MutableState']>): Memory {
|
|
49
|
+
return new Memory({
|
|
50
|
+
...this.state,
|
|
51
|
+
...newstate,
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
equals(other: unknown): boolean {
|
|
56
|
+
return other instanceof Memory && isDeepEqual(this.state, other.state)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
diff(state: Memory) {
|
|
60
|
+
const current = this
|
|
61
|
+
return {
|
|
62
|
+
added: {
|
|
63
|
+
elements: toArray(differenceSet(state.elements, current.elements)),
|
|
64
|
+
explicits: toArray(differenceSet(state.explicits, current.explicits)),
|
|
65
|
+
final: toArray(differenceSet(state.final, current.final)),
|
|
66
|
+
connections: toArray(differenceConnections(state.connections, current.connections)),
|
|
67
|
+
},
|
|
68
|
+
removed: {
|
|
69
|
+
elements: toArray(differenceSet(current.elements, state.elements)),
|
|
70
|
+
explicits: toArray(differenceSet(current.explicits, state.explicits)),
|
|
71
|
+
final: toArray(differenceSet(current.final, state.final)),
|
|
72
|
+
connections: differenceConnections(current.connections, state.connections),
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public override toString(): string {
|
|
78
|
+
return [
|
|
79
|
+
'final:',
|
|
80
|
+
...[...this.final].map(e => ' ' + e.id),
|
|
81
|
+
'connections:',
|
|
82
|
+
...this.connections.map(c => ' ' + c.expression),
|
|
83
|
+
].join('\n')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
[customInspectSymbol](_depth: unknown, _inspectOptions: unknown, _inspect: unknown) {
|
|
87
|
+
const asString = this.toString()
|
|
88
|
+
|
|
89
|
+
// // Trick so that node displays the name of the constructor
|
|
90
|
+
// Object.defineProperty(asString, 'constructor', {
|
|
91
|
+
// value: this.constructor,
|
|
92
|
+
// enumerable: false,
|
|
93
|
+
// })
|
|
94
|
+
|
|
95
|
+
return asString
|
|
96
|
+
}
|
|
97
|
+
}
|