@likec4/core 1.17.1 → 1.19.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.
Files changed (184) hide show
  1. package/LICENSE +1 -1
  2. package/dist/builder/index.d.mts +502 -0
  3. package/dist/builder/index.d.ts +502 -0
  4. package/dist/builder/index.js +871 -0
  5. package/dist/compute-view/index.d.mts +22 -0
  6. package/dist/compute-view/index.d.ts +22 -0
  7. package/dist/compute-view/index.js +78 -0
  8. package/dist/index.d.mts +10 -919
  9. package/dist/index.d.ts +10 -919
  10. package/dist/{index.mjs → index.js} +7 -1688
  11. package/dist/model/index.d.mts +254 -0
  12. package/dist/model/index.d.ts +254 -0
  13. package/dist/model/index.js +1 -0
  14. package/dist/shared/chunk-RAAYCPUM.M-JWF7SS.js +21 -0
  15. package/dist/shared/core.BIfgabEw.d.ts +719 -0
  16. package/dist/shared/core.BZ9Msq7w.d.mts +719 -0
  17. package/dist/shared/{core.CoLQkcYL.d.cts → core.C05RDLhi.d.ts} +460 -362
  18. package/dist/shared/{core.CoLQkcYL.d.mts → core.CmYMqgha.d.mts} +460 -362
  19. package/dist/shared/core.D6-fWbM3.d.ts +127 -0
  20. package/dist/shared/core.D74xkKkG.d.mts +148 -0
  21. package/dist/shared/core.D74xkKkG.d.ts +148 -0
  22. package/dist/shared/core.sLaWHBjR.d.mts +127 -0
  23. package/dist/shared/index.-BdzdI2C.js +7443 -0
  24. package/dist/shared/object_hash.CE_oF3I3.js +1222 -0
  25. package/dist/shared/{core.BXTs-8n7.mjs → view.CyZrG8BJ.js} +108 -47
  26. package/dist/types/index.d.mts +71 -1
  27. package/dist/types/index.d.ts +71 -1
  28. package/dist/types/index.js +20 -0
  29. package/dist/utils/index.d.mts +404 -0
  30. package/dist/utils/index.d.ts +404 -0
  31. package/dist/utils/index.js +330 -0
  32. package/package.json +59 -47
  33. package/src/builder/Builder-style1.test-d.ts +190 -0
  34. package/src/builder/Builder-style2.test-d.ts +175 -0
  35. package/src/builder/Builder.deployment.ts +224 -0
  36. package/src/builder/Builder.deploymentModel.ts +385 -0
  37. package/src/builder/Builder.element.ts +19 -19
  38. package/src/builder/Builder.model.ts +67 -42
  39. package/src/builder/Builder.ts +533 -367
  40. package/src/builder/Builder.view-common.ts +234 -0
  41. package/src/builder/Builder.view-deployment.ts +135 -0
  42. package/src/builder/Builder.view-element.ts +189 -0
  43. package/src/builder/Builder.views.ts +109 -33
  44. package/src/builder/Builder.with.ts +475 -0
  45. package/src/builder/__snapshots__/Builder-style1.spec.ts.snap +374 -0
  46. package/src/builder/__snapshots__/Builder-style2.spec.ts.snap +183 -0
  47. package/src/builder/__snapshots__/Builder.spec.ts.snap +374 -0
  48. package/src/builder/_types.ts +113 -18
  49. package/src/builder/index.ts +10 -4
  50. package/src/colors/index.ts +2 -2
  51. package/src/compute-view/compute-view.ts +83 -0
  52. package/src/compute-view/deployment-view/__test__/TestHelper.ts +236 -0
  53. package/src/compute-view/deployment-view/__test__/fixture.ts +198 -0
  54. package/src/compute-view/deployment-view/_types.ts +37 -0
  55. package/src/compute-view/deployment-view/clean-connections.ts +238 -0
  56. package/src/compute-view/deployment-view/compute.ts +125 -0
  57. package/src/compute-view/deployment-view/memory/index.ts +9 -0
  58. package/src/compute-view/deployment-view/memory/memory.ts +97 -0
  59. package/src/compute-view/deployment-view/predicates/elements.ts +144 -0
  60. package/src/compute-view/deployment-view/predicates/relation-direct.ts +237 -0
  61. package/src/compute-view/deployment-view/predicates/relation-in-out.ts +61 -0
  62. package/src/compute-view/deployment-view/predicates/relation-incoming.ts +95 -0
  63. package/src/compute-view/deployment-view/predicates/relation-outgoing.ts +88 -0
  64. package/src/compute-view/deployment-view/predicates/wildcard.ts +29 -0
  65. package/src/compute-view/deployment-view/stages/stage-exclude.ts +5 -0
  66. package/src/compute-view/deployment-view/stages/stage-final.ts +245 -0
  67. package/src/compute-view/deployment-view/stages/stage-include.ts +95 -0
  68. package/src/compute-view/deployment-view/utils.ts +276 -0
  69. package/src/compute-view/dynamic-view/__test__/fixture.ts +58 -0
  70. package/src/compute-view/dynamic-view/compute.ts +324 -0
  71. package/src/compute-view/element-view/__test__/TestHelper.ts +258 -0
  72. package/src/compute-view/element-view/__test__/__snapshots__/legacy.spec.ts.snap +458 -0
  73. package/src/compute-view/element-view/__test__/fixture.ts +675 -0
  74. package/src/compute-view/element-view/_types.ts +48 -0
  75. package/src/compute-view/element-view/clean-connections.ts +113 -0
  76. package/src/compute-view/element-view/compute.ts +315 -0
  77. package/src/compute-view/element-view/memory/NodeGroup.ts +31 -0
  78. package/src/compute-view/element-view/memory/index.ts +10 -0
  79. package/src/compute-view/element-view/memory/memory.ts +177 -0
  80. package/src/compute-view/element-view/memory/stage-exclude.ts +59 -0
  81. package/src/compute-view/element-view/memory/stage-final.ts +129 -0
  82. package/src/compute-view/element-view/memory/stage-include.ts +128 -0
  83. package/src/compute-view/element-view/predicates/_utils.ts +86 -0
  84. package/src/compute-view/element-view/predicates/element-expand.ts +33 -0
  85. package/src/compute-view/element-view/predicates/element-kind-tag.ts +26 -0
  86. package/src/compute-view/element-view/predicates/element-ref.ts +37 -0
  87. package/src/compute-view/element-view/predicates/relation-direct.ts +309 -0
  88. package/src/compute-view/element-view/predicates/relation-in-out.ts +66 -0
  89. package/src/compute-view/element-view/predicates/relation-in.ts +118 -0
  90. package/src/compute-view/element-view/predicates/relation-out.ts +116 -0
  91. package/src/compute-view/element-view/predicates/wildcard.ts +88 -0
  92. package/src/compute-view/element-view/utils.ts +59 -0
  93. package/src/compute-view/index.ts +4 -0
  94. package/src/compute-view/memory/AbstractMemory.ts +45 -0
  95. package/src/compute-view/memory/AbstractStageExclude.ts +216 -0
  96. package/src/compute-view/memory/AbstractStageInclude.ts +200 -0
  97. package/src/compute-view/memory/_types.ts +147 -0
  98. package/src/compute-view/memory/index.ts +5 -0
  99. package/src/compute-view/memory/ops.ts +43 -0
  100. package/src/compute-view/utils/__snapshots__/sortNodes.spec.ts.snap +113 -0
  101. package/src/compute-view/utils/ancestorsOfNode.ts +19 -0
  102. package/src/compute-view/utils/applyCustomElementProperties.ts +66 -0
  103. package/src/compute-view/utils/applyCustomRelationProperties.ts +41 -0
  104. package/src/compute-view/utils/applyViewRuleStyles.ts +53 -0
  105. package/src/compute-view/utils/buildComputedNodes.ts +148 -0
  106. package/src/compute-view/utils/buildElementNotations.ts +63 -0
  107. package/src/compute-view/utils/elementExpressionToPredicate.ts +50 -0
  108. package/src/compute-view/utils/link-nodes-with-edges.ts +54 -0
  109. package/src/compute-view/utils/merge-props-from-relationships.ts +128 -0
  110. package/src/compute-view/utils/relationExpressionToPredicates.ts +55 -0
  111. package/src/compute-view/utils/resolve-extended-views.ts +69 -0
  112. package/src/compute-view/utils/resolve-global-rules.ts +83 -0
  113. package/src/compute-view/utils/topological-sort.ts +256 -0
  114. package/src/compute-view/utils/uniqueTags.test.ts +42 -0
  115. package/src/compute-view/utils/uniqueTags.ts +21 -0
  116. package/src/compute-view/utils/view-hash.ts +27 -0
  117. package/src/compute-view/utils/with-readable-edges.ts +36 -0
  118. package/src/index.ts +22 -12
  119. package/src/model/DeploymentElementModel.ts +599 -0
  120. package/src/model/DeploymentModel.ts +370 -0
  121. package/src/model/ElementModel.ts +221 -0
  122. package/src/model/LikeC4Model.test-d.ts +152 -0
  123. package/src/model/LikeC4Model.ts +336 -457
  124. package/src/model/RelationModel.ts +85 -0
  125. package/src/model/__test__/fixture.ts +149 -338
  126. package/src/model/connection/Connection.ts +72 -0
  127. package/src/model/connection/deployment/DeploymentConnectionModel.ts +178 -0
  128. package/src/model/connection/deployment/find.ts +120 -0
  129. package/src/model/connection/deployment/index.ts +8 -0
  130. package/src/model/connection/index.ts +22 -0
  131. package/src/model/connection/model/ConnectionModel.ts +157 -0
  132. package/src/model/connection/model/find.ts +117 -0
  133. package/src/model/connection/model/index.ts +8 -0
  134. package/src/model/connection/ops.ts +274 -0
  135. package/src/model/guards.ts +9 -0
  136. package/src/model/index.ts +46 -2
  137. package/src/model/types.ts +112 -15
  138. package/src/model/view/EdgeModel.ts +103 -0
  139. package/src/model/view/LikeC4ViewModel.ts +226 -0
  140. package/src/model/view/NodeModel.ts +229 -0
  141. package/src/types/_common.ts +55 -1
  142. package/src/types/deployments.ts +169 -0
  143. package/src/types/element.ts +9 -5
  144. package/src/types/expression-v2.ts +172 -0
  145. package/src/types/expression.ts +17 -11
  146. package/src/types/global.ts +3 -3
  147. package/src/types/index.ts +220 -12
  148. package/src/types/model.ts +75 -12
  149. package/src/types/operators.ts +1 -1
  150. package/src/types/overview-graph.ts +2 -2
  151. package/src/types/relation.ts +18 -14
  152. package/src/types/theme.ts +0 -1
  153. package/src/types/view-notation.ts +2 -2
  154. package/src/types/view.ts +117 -41
  155. package/src/utils/{commonHead.ts → common-head.ts} +2 -0
  156. package/src/utils/const.ts +1 -0
  157. package/src/utils/fqn.ts +203 -56
  158. package/src/utils/getOrCreate.ts +42 -0
  159. package/src/utils/graphlib.ts +10 -0
  160. package/src/utils/index.ts +74 -6
  161. package/src/utils/iterable/_types.ts +5 -0
  162. package/src/utils/iterable/filter.ts +55 -0
  163. package/src/utils/iterable/flat.ts +17 -0
  164. package/src/utils/iterable/index.ts +12 -0
  165. package/src/utils/iterable/map.ts +40 -0
  166. package/src/utils/iterable/reduce.ts +24 -0
  167. package/src/utils/iterable/some.ts +36 -0
  168. package/src/utils/iterable/to.ts +19 -0
  169. package/src/utils/iterable/unique.ts +29 -0
  170. package/src/utils/object-hash.ts +3 -0
  171. package/src/utils/relations.ts +3 -64
  172. package/src/utils/set.ts +78 -0
  173. package/src/utils/string-hash.ts +16 -0
  174. package/dist/index.cjs +0 -5452
  175. package/dist/index.d.cts +0 -1053
  176. package/dist/shared/core.CoLQkcYL.d.ts +0 -734
  177. package/dist/shared/core.DIYZvpVg.cjs +0 -353
  178. package/dist/types/index.cjs +0 -70
  179. package/dist/types/index.d.cts +0 -2
  180. package/dist/types/index.mjs +0 -1
  181. package/src/builder/Builder.test-d.ts +0 -69
  182. package/src/builder/Builder.view.ts +0 -358
  183. package/src/model/LikeC4DiagramModel.ts +0 -338
  184. package/src/model/LikeC4ViewModel.ts +0 -320
@@ -0,0 +1,238 @@
1
+ import DefaultMap from 'mnemonist/default-map'
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'
25
+ import { RelationshipsAccum } from '../../model/DeploymentElementModel'
26
+ import type { RelationshipModel } from '../../model/RelationModel'
27
+ import { imap, toArray } from '../../utils/iterable'
28
+ import { intersection, union } from '../../utils/set'
29
+ import type { Connection, Connections } from './_types'
30
+
31
+ const filterEmptyConnection = filter((c: DeploymentConnectionModel<any>) => c.nonEmpty())
32
+
33
+ type MapOfExcludesFromConnection = DefaultMap<DeploymentConnectionModel, Set<RelationshipModel>>
34
+ function findCrossBoundarySameSourceOrTarget(connections: Connections): MapOfExcludesFromConnection {
35
+ // Also find connections based on same relation
36
+ const groupedByRelation = new DefaultMap<RelationshipModel, Array<DeploymentConnectionModel>>(() => [])
37
+ for (const conn of connections) {
38
+ for (const relation of conn.relations.model) {
39
+ groupedByRelation.get(relation).push(conn)
40
+ }
41
+ }
42
+
43
+ // Aggreate excluded relations for each connection
44
+ const excludedRelations = new DefaultMap<
45
+ DeploymentConnectionModel,
46
+ Set<RelationshipModel>
47
+ >(() => new Set())
48
+
49
+ // In each group, find connected to same leaf
50
+ for (const [relation, sameRelationGroup] of groupedByRelation) {
51
+ if (!hasAtLeast(sameRelationGroup, 2)) {
52
+ continue
53
+ }
54
+
55
+ // Outgoing from same source
56
+ pipe(
57
+ sameRelationGroup,
58
+ flatMap(conn => [
59
+ { group: `$source-${conn.source.id}`, conn },
60
+ { group: `$target-${conn.target.id}`, conn },
61
+ ]),
62
+ groupBy(prop('group')),
63
+ values(),
64
+ filter(hasAtLeast(2)),
65
+ forEach(
66
+ // In each group, sort by hierarchy, first are deepest
67
+ piped(
68
+ map(prop('conn')),
69
+ sortConnectionsByBoundaryHierarchy('desc'),
70
+ // Drop first, as it is the deepest
71
+ // Drop if boundary is same as previous
72
+ dropWhile((conn, i, all) => i === 0 || conn.boundary === all[i - 1]!.boundary),
73
+ // Drop relations from boundaries above
74
+ forEach((conn) => {
75
+ excludedRelations.get(conn).add(relation)
76
+ }),
77
+ ),
78
+ ),
79
+ )
80
+ }
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
+
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(
168
+ connections,
169
+ findCrossBoundaryConnections(connections),
170
+ )
171
+ }
172
+
173
+ /**
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
177
+ *
178
+ * @returns Array of redundant connections that can be safely removed
179
+ */
180
+ export function findRedundantConnections(connections: Connections): Array<Connection> {
181
+ return pipe(
182
+ connections,
183
+ mergeConnections,
184
+ reduce((redundants, connection) => {
185
+ const { source, target, relations } = connection
186
+
187
+ // Connection between instances is always explicit
188
+ if (source.isInstance() && target.isInstance()) {
189
+ return redundants
190
+ }
191
+
192
+ let redundantAccum = RelationshipsAccum.empty()
193
+
194
+ if (source.isDeploymentNode() && target.isDeploymentNode()) {
195
+ const [reversed] = findConnection(target, source, 'directed')
196
+ if (reversed) {
197
+ redundantAccum = relations.intersect(reversed.relations)
198
+ }
199
+ }
200
+
201
+ for (const c of connections) {
202
+ if (isNestedConnection(c, connection)) {
203
+ redundantAccum = redundantAccum.union(
204
+ relations.intersect(c.relations),
205
+ )
206
+ }
207
+ }
208
+
209
+ if (redundantAccum.nonEmpty) {
210
+ redundants.push(
211
+ new DeploymentConnectionModel(
212
+ source,
213
+ target,
214
+ redundantAccum,
215
+ ),
216
+ )
217
+ }
218
+
219
+ return redundants
220
+ }, [] as DeploymentConnectionModel[]),
221
+ )
222
+ }
223
+
224
+ /**
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
230
+ */
231
+ export function cleanRedundantRelationships(
232
+ connections: Connections,
233
+ ): Array<Connection> {
234
+ return differenceConnections(
235
+ connections,
236
+ findRedundantConnections(connections),
237
+ )
238
+ }
@@ -0,0 +1,125 @@
1
+ import { findLast, map } from 'remeda'
2
+ import { nonexhaustive } from '../../errors'
3
+ import { type LikeC4DeploymentModel, LikeC4Model } from '../../model'
4
+ import type { AnyAux } from '../../model/types'
5
+ import type { DeploymentViewRule } from '../../types'
6
+ import {
7
+ type ComputedDeploymentView,
8
+ type DeploymentView,
9
+ FqnExpr,
10
+ isViewRuleAutoLayout,
11
+ isViewRulePredicate,
12
+ } from '../../types'
13
+ import { RelationExpr } from '../../types/expression-v2'
14
+ import { buildElementNotations } from '../utils/buildElementNotations'
15
+ import { linkNodesWithEdges } from '../utils/link-nodes-with-edges'
16
+ import { topologicalSort } from '../utils/topological-sort'
17
+ import { calcViewLayoutHash } from '../utils/view-hash'
18
+ import { Memory } from './memory'
19
+ import { DeploymentRefPredicate } from './predicates/elements'
20
+ import { DirectRelationPredicate } from './predicates/relation-direct'
21
+ import { InOutRelationPredicate } from './predicates/relation-in-out'
22
+ import { IncomingRelationPredicate } from './predicates/relation-incoming'
23
+ import { OutgoingRelationPredicate } from './predicates/relation-outgoing'
24
+ import { WildcardPredicate } from './predicates/wildcard'
25
+ import { StageFinal } from './stages/stage-final'
26
+ import { applyDeploymentViewRuleStyles, buildNodes, toComputedEdges } from './utils'
27
+
28
+ export function processPredicates<M extends AnyAux>(
29
+ model: LikeC4DeploymentModel<M>,
30
+ rules: DeploymentViewRule[],
31
+ ) {
32
+ let memory = Memory.empty()
33
+
34
+ for (const rule of rules) {
35
+ if (isViewRulePredicate(rule)) {
36
+ const op = 'include' in rule ? 'include' : 'exclude'
37
+ const exprs = rule.include ?? rule.exclude
38
+ for (const expr of exprs) {
39
+ let stage = op === 'include' ? memory.stageInclude(expr) : memory.stageExclude(expr)
40
+ const ctx = { model, stage, memory }
41
+ switch (true) {
42
+ case FqnExpr.isModelRef(expr):
43
+ // Ignore model refs in deployment view
44
+ break
45
+ case FqnExpr.isDeploymentRef(expr):
46
+ stage = DeploymentRefPredicate[op]({ ...ctx, expr } as any) ?? stage
47
+ break
48
+ case FqnExpr.isWildcard(expr):
49
+ stage = WildcardPredicate[op]({ ...ctx, expr } as any) ?? stage
50
+ break
51
+ case RelationExpr.isDirect(expr):
52
+ stage = DirectRelationPredicate[op]({ ...ctx, expr } as any) ?? stage
53
+ break
54
+ case RelationExpr.isInOut(expr):
55
+ stage = InOutRelationPredicate[op]({ ...ctx, expr } as any) ?? stage
56
+ break
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
62
+ break
63
+ default:
64
+ nonexhaustive(expr)
65
+ }
66
+ memory = stage.commit()
67
+ }
68
+ }
69
+ }
70
+ return StageFinal.for(memory).commit()
71
+ }
72
+
73
+ export function computeDeploymentView<M extends AnyAux>(
74
+ likec4model: LikeC4Model<M>,
75
+ {
76
+ docUri: _docUri, // exclude docUri
77
+ rules, // exclude rules
78
+ ...view
79
+ }: DeploymentView,
80
+ ): ComputedDeploymentView<M['ViewId']> {
81
+ let memory = processPredicates<M>(likec4model.deployment, rules)
82
+
83
+ const nodesMap = buildNodes(memory)
84
+
85
+ const computedEdges = toComputedEdges(memory.connections)
86
+
87
+ linkNodesWithEdges(nodesMap, computedEdges)
88
+
89
+ const sorted = topologicalSort({
90
+ nodes: nodesMap,
91
+ edges: computedEdges,
92
+ })
93
+
94
+ const nodes = applyDeploymentViewRuleStyles(
95
+ rules,
96
+ sorted.nodes,
97
+ )
98
+
99
+ const autoLayoutRule = findLast(rules, isViewRuleAutoLayout)
100
+
101
+ const elementNotations = buildElementNotations(nodes)
102
+
103
+ return calcViewLayoutHash({
104
+ ...view,
105
+ autoLayout: {
106
+ direction: autoLayoutRule?.direction ?? 'TB',
107
+ ...(autoLayoutRule?.nodeSep && { nodeSep: autoLayoutRule.nodeSep }),
108
+ ...(autoLayoutRule?.rankSep && { rankSep: autoLayoutRule.rankSep }),
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
+ }),
124
+ })
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
+ }
@@ -0,0 +1,144 @@
1
+ import { pipe, reduce } from 'remeda'
2
+ import { isDeployedInstance } from '../../../model'
3
+ import { findConnectionsBetween, findConnectionsWithin } from '../../../model/connection/deployment'
4
+ import type { DeploymentElementModel, DeploymentNodeModel } from '../../../model/DeploymentElementModel'
5
+ import type { FqnExpr } from '../../../types'
6
+ import type { Elem, IncludePredicateCtx, PredicateExecutor } from '../_types'
7
+ import { cleanCrossBoundary, cleanRedundantRelationships } from '../clean-connections'
8
+ import type { StageInclude } from '../memory'
9
+ import { deploymentExpressionToPredicate } from '../utils'
10
+
11
+ export const DeploymentRefPredicate: PredicateExecutor<FqnExpr.DeploymentRef> = {
12
+ include: (ctx) => {
13
+ const { expr } = ctx
14
+ const el = ctx.model.element(expr.ref.deployment)
15
+
16
+ if (isDeployedInstance(el)) {
17
+ ctx.stage.addExplicit(el)
18
+ ctx.stage.connectWithExisting(el)
19
+ return ctx.stage
20
+ }
21
+
22
+ switch (true) {
23
+ case expr.selector === 'expanded':
24
+ includeDeployedNodeWithExpanded(el, ctx)
25
+ break
26
+ case expr.selector === 'children':
27
+ includeDeployedNodeChildren(el, ctx)
28
+ break
29
+ case expr.selector === 'descendants':
30
+ includeDeployedNodeDescendants(el, ctx)
31
+ break
32
+ default: {
33
+ ctx.stage.addExplicit(el)
34
+ ctx.stage.connectWithExisting(el)
35
+ }
36
+ }
37
+ return ctx.stage
38
+ },
39
+
40
+ exclude: ({ expr, stage, memory }) => {
41
+ const exprPredicate = deploymentExpressionToPredicate(expr)
42
+ stage.exclude([...memory.elements].filter(exprPredicate))
43
+ return stage
44
+ },
45
+ }
46
+
47
+ /**
48
+ * include node.*
49
+ */
50
+ function includeDeployedNodeChildren(
51
+ node: DeploymentNodeModel,
52
+ { stage }: IncludePredicateCtx,
53
+ ) {
54
+ const children = [...node.children()]
55
+ if (children.length === 0) {
56
+ return
57
+ }
58
+ stage.addImplicit(node)
59
+
60
+ stage.addConnections(findConnectionsWithin(children))
61
+ stage.connectWithExisting(children)
62
+
63
+ stage.addExplicit(children)
64
+ }
65
+
66
+ /**
67
+ * include node._
68
+ */
69
+ function includeDeployedNodeWithExpanded(
70
+ node: DeploymentNodeModel,
71
+ { memory, stage }: IncludePredicateCtx,
72
+ ) {
73
+ const children = [...node.children()]
74
+ stage.addImplicit(node)
75
+ stage.connectWithExisting(node)
76
+
77
+ let hasConnectionsWithVisible = false
78
+ for (const child of children) {
79
+ if (findConnectionsBetween(child, memory.elements).length > 0) {
80
+ hasConnectionsWithVisible = true
81
+ break
82
+ }
83
+ }
84
+
85
+ if (hasConnectionsWithVisible) {
86
+ stage.connectWithExisting(children, 'in')
87
+ stage.addConnections(findConnectionsWithin(children))
88
+ stage.connectWithExisting(children, 'out')
89
+ }
90
+ stage.addImplicit(children)
91
+
92
+ if (stage.connections.length > 0) {
93
+ stage.addExplicit(node)
94
+ }
95
+ }
96
+
97
+ /**
98
+ * include node.**
99
+ */
100
+ function includeDeployedNodeDescendants(
101
+ node: DeploymentNodeModel,
102
+ { stage }: IncludePredicateCtx,
103
+ ) {
104
+ const dfs = (node: DeploymentNodeModel): DeploymentElementModel[] => {
105
+ const children = [] as DeploymentElementModel[]
106
+ for (const child of node.children()) {
107
+ if (child.isDeploymentNode()) {
108
+ children.push(...dfs(child))
109
+ }
110
+ children.push(child)
111
+ }
112
+ stage.connectWithExisting(children, 'in')
113
+ stage.addConnections(findConnectionsWithin(children))
114
+ stage.addImplicit(children)
115
+ return children
116
+ }
117
+
118
+ const descendants = dfs(node)
119
+ if (descendants.length === 0) {
120
+ return
121
+ }
122
+ stage.connectWithExisting(descendants, 'out')
123
+ // stage.addImplicit(descendants)
124
+
125
+ const allconnected = findConnectedElements(stage)
126
+ descendants.forEach((desc) => {
127
+ if (allconnected.has(desc)) {
128
+ stage.addExplicit(desc)
129
+ }
130
+ })
131
+ }
132
+
133
+ function findConnectedElements(stage: StageInclude) {
134
+ return pipe(
135
+ stage.mergedConnections(),
136
+ cleanCrossBoundary,
137
+ cleanRedundantRelationships,
138
+ reduce((acc, c) => {
139
+ acc.add(c.source)
140
+ acc.add(c.target)
141
+ return acc
142
+ }, new Set<Elem>()),
143
+ )
144
+ }