@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.
Files changed (177) 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 +9 -78
  6. package/dist/compute-view/index.d.ts +9 -78
  7. package/dist/compute-view/index.js +78 -0
  8. package/dist/index.d.mts +9 -730
  9. package/dist/index.d.ts +9 -730
  10. package/dist/{index.mjs → index.js} +7 -784
  11. package/dist/model/index.d.mts +218 -606
  12. package/dist/model/index.d.ts +218 -606
  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.DMYY7C8s.d.mts → core.C05RDLhi.d.ts} +179 -230
  18. package/dist/shared/{core.DMYY7C8s.d.ts → core.CmYMqgha.d.mts} +179 -230
  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.DafVOuVd.js +7443 -0
  24. package/dist/shared/object_hash.CE_oF3I3.js +1222 -0
  25. package/dist/shared/view.CyZrG8BJ.js +354 -0
  26. package/dist/types/index.d.mts +4 -2
  27. package/dist/types/index.d.ts +4 -2
  28. package/dist/types/index.js +20 -0
  29. package/dist/utils/index.d.mts +419 -0
  30. package/dist/utils/index.d.ts +419 -0
  31. package/dist/utils/index.js +347 -0
  32. package/package.json +47 -33
  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 +25 -20
  36. package/src/builder/Builder.deploymentModel.ts +80 -55
  37. package/src/builder/Builder.element.ts +2 -0
  38. package/src/builder/Builder.model.ts +57 -43
  39. package/src/builder/Builder.ts +412 -431
  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 +28 -5
  49. package/src/builder/index.ts +10 -4
  50. package/src/colors/index.ts +2 -2
  51. package/src/compute-view/compute-view.ts +45 -54
  52. package/src/compute-view/deployment-view/__test__/TestHelper.ts +236 -0
  53. package/src/compute-view/deployment-view/__test__/fixture.ts +50 -62
  54. package/src/compute-view/deployment-view/_types.ts +26 -18
  55. package/src/compute-view/deployment-view/clean-connections.ts +173 -164
  56. package/src/compute-view/deployment-view/compute.ts +51 -37
  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 +183 -76
  61. package/src/compute-view/deployment-view/predicates/relation-in-out.ts +38 -20
  62. package/src/compute-view/deployment-view/predicates/relation-incoming.ts +72 -23
  63. package/src/compute-view/deployment-view/predicates/relation-outgoing.ts +61 -22
  64. package/src/compute-view/deployment-view/predicates/wildcard.ts +15 -15
  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 +95 -31
  69. package/src/compute-view/dynamic-view/__test__/fixture.ts +4 -4
  70. package/src/compute-view/dynamic-view/compute.ts +59 -45
  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 +64 -62
  73. package/src/compute-view/element-view/__test__/fixture.ts +147 -117
  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 +264 -683
  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 +3 -4
  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/buildComputedNodes.ts +4 -4
  101. package/src/compute-view/utils/elementExpressionToPredicate.ts +1 -3
  102. package/src/compute-view/utils/link-nodes-with-edges.ts +3 -7
  103. package/src/compute-view/utils/{derive-edge-props-from-relationships.ts → merge-props-from-relationships.ts} +54 -23
  104. package/src/compute-view/utils/relationExpressionToPredicates.ts +2 -2
  105. package/src/compute-view/utils/resolve-extended-views.ts +44 -40
  106. package/src/compute-view/utils/resolve-global-rules.ts +6 -6
  107. package/src/compute-view/utils/topological-sort.ts +167 -71
  108. package/src/compute-view/utils/with-readable-edges.ts +14 -9
  109. package/src/index.ts +22 -16
  110. package/src/model/DeploymentElementModel.ts +150 -85
  111. package/src/model/DeploymentModel.ts +27 -21
  112. package/src/model/ElementModel.ts +51 -25
  113. package/src/model/LikeC4Model.test-d.ts +4 -3
  114. package/src/model/LikeC4Model.ts +98 -42
  115. package/src/model/RelationModel.ts +5 -4
  116. package/src/model/__test__/fixture.ts +5 -9
  117. package/src/model/connection/Connection.ts +72 -0
  118. package/src/model/connection/deployment/DeploymentConnectionModel.ts +178 -0
  119. package/src/model/connection/{deployment.ts → deployment/find.ts} +14 -18
  120. package/src/model/connection/deployment/index.ts +8 -0
  121. package/src/model/connection/index.ts +22 -0
  122. package/src/model/connection/model/ConnectionModel.ts +157 -0
  123. package/src/model/connection/{model.ts → model/find.ts} +43 -49
  124. package/src/model/connection/model/index.ts +8 -0
  125. package/src/model/connection/ops.ts +274 -0
  126. package/src/model/guards.ts +9 -0
  127. package/src/model/index.ts +45 -3
  128. package/src/model/types.ts +35 -18
  129. package/src/model/view/EdgeModel.ts +6 -6
  130. package/src/model/view/LikeC4ViewModel.ts +14 -8
  131. package/src/model/view/NodeModel.ts +6 -5
  132. package/src/types/_common.ts +53 -1
  133. package/src/types/deployments.ts +88 -156
  134. package/src/types/element.ts +2 -2
  135. package/src/types/expression-v2.ts +172 -0
  136. package/src/types/expression.ts +9 -5
  137. package/src/types/global.ts +3 -3
  138. package/src/types/index.ts +220 -13
  139. package/src/types/model.ts +28 -24
  140. package/src/types/operators.ts +1 -1
  141. package/src/types/relation.ts +14 -10
  142. package/src/types/theme.ts +0 -1
  143. package/src/types/view.ts +20 -18
  144. package/src/utils/const.ts +1 -0
  145. package/src/utils/fqn.ts +140 -49
  146. package/src/utils/getOrCreate.ts +1 -1
  147. package/src/utils/index.ts +75 -9
  148. package/src/utils/iterable/_types.ts +5 -0
  149. package/src/utils/iterable/filter.ts +55 -0
  150. package/src/utils/iterable/find.ts +36 -0
  151. package/src/utils/iterable/flat.ts +17 -0
  152. package/src/utils/iterable/index.ts +13 -0
  153. package/src/utils/iterable/map.ts +40 -0
  154. package/src/utils/iterable/reduce.ts +24 -0
  155. package/src/utils/iterable/some.ts +36 -0
  156. package/src/utils/iterable/to.ts +19 -0
  157. package/src/utils/iterable/unique.ts +29 -0
  158. package/src/utils/relations.ts +3 -64
  159. package/src/utils/set.ts +11 -9
  160. package/src/utils/string-hash.ts +1 -1
  161. package/dist/compute-view/index.mjs +0 -79
  162. package/dist/model/index.mjs +0 -2
  163. package/dist/shared/core.BBgiAAv1.mjs +0 -144
  164. package/dist/shared/core.BPyJvuMU.mjs +0 -2420
  165. package/dist/shared/core.BQ4AbfvB.mjs +0 -9228
  166. package/dist/shared/core.k12s_keD.mjs +0 -203
  167. package/dist/types/index.mjs +0 -21
  168. package/src/builder/Builder.test-d.ts +0 -124
  169. package/src/builder/Builder.view.ts +0 -543
  170. package/src/compute-view/LikeC4ModelGraph.ts +0 -330
  171. package/src/compute-view/deployment-view/Memory.ts +0 -108
  172. package/src/compute-view/deployment-view/Stage.ts +0 -145
  173. package/src/compute-view/deployment-view/predicates/deployment-ref.ts +0 -146
  174. package/src/compute-view/element-view/predicates.ts +0 -517
  175. package/src/compute-view/utils/sortNodes.ts +0 -101
  176. package/src/model/connection/ConnectionModel.ts +0 -94
  177. package/src/model/connection/DeploymentConnectionModel.ts +0 -99
@@ -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
+ }
@@ -1,130 +1,237 @@
1
- import { identity, isNonNullish } from 'remeda'
1
+ import { filter, flatMap, isNonNullish, map, pipe } from 'remeda'
2
2
  import { invariant } from '../../../errors'
3
3
  import type { LikeC4DeploymentModel } from '../../../model'
4
- import { findConnectionsBetween } from '../../../model/connection/deployment'
5
4
  import type { DeploymentConnectionModel } from '../../../model/connection/deployment'
6
- import { DeploymentElementModel } from '../../../model/DeploymentElementModel'
7
- import { DeploymentElementExpression, type DeploymentRelationExpression } from '../../../types/deployments'
8
- import { union } from '../../../utils/set'
9
- import type { PredicateExecutor } from '../_types'
10
- import { deploymentExpressionToPredicate, resolveElements } from '../utils'
5
+ import { findConnection, findConnectionsBetween, findConnectionsWithin } from '../../../model/connection/deployment'
6
+ import { findConnectionsBetween as findModelConnectionsBetween } from '../../../model/connection/model'
7
+ import type { ConnectionModel } from '../../../model/connection/model/ConnectionModel'
8
+ import type { DeploymentElementModel } from '../../../model/DeploymentElementModel'
9
+ import type { RelationshipModel } from '../../../model/RelationModel'
10
+ import type { AnyAux } from '../../../model/types'
11
+ import { type RelationExpr, FqnExpr } from '../../../types'
12
+ import { hasIntersection, intersection } from '../../../utils/set'
13
+ import type { ExcludePredicateCtx, PredicateExecutor } from '../_types'
14
+ import type { StageExclude } from '../memory'
15
+ import { deploymentExpressionToPredicate, resolveElements, resolveModelElements } from '../utils'
16
+ import { filterIncomingConnections, resolveAllImcomingRelations } from './relation-incoming'
17
+ import { filterOutgoingConnections, resolveAllOutgoingRelations } from './relation-outgoing'
11
18
 
12
- const isRef = DeploymentElementExpression.isRef
19
+ // const isRef = DeploymentElementExpression.isRef
13
20
 
14
21
  export const resolveAscendingSiblings = (element: DeploymentElementModel) => {
15
22
  const siblings = new Set<DeploymentElementModel>()
16
- for (let sibling of element.ascendingSiblings()) {
17
- if (element.isInstance() && sibling.isDeploymentNode()) {
18
- // we flatten nodes that contain only one instance
19
- sibling = sibling.onlyOneInstance() ?? sibling
20
- }
23
+ for (let sibling of element.descendingSiblings()) {
24
+ // TODO: investigate if this is necessary
25
+ // if (element.isInstance() && sibling.isDeploymentNode()) {
26
+ // // we flatten nodes that contain only one instance
27
+ // sibling = sibling.onlyOneInstance() ?? sibling
28
+ // }
21
29
  siblings.add(sibling)
22
30
  }
23
31
  return siblings
24
32
  }
25
33
 
26
- const resolveIfWildcard = (model: LikeC4DeploymentModel, nonWildcard: DeploymentElementExpression.Ref) => {
34
+ const resolveWildcard = (model: LikeC4DeploymentModel, nonWildcard: FqnExpr.DeploymentRef) => {
27
35
  const sources = resolveElements(model, nonWildcard)
28
- const [head, ...rest] = sources.map(s => resolveAscendingSiblings(s))
29
- if (head) {
30
- let targets = rest.length > 0 ? union(head, ...rest) : head
31
- return [sources, [...targets]] as const
32
- }
33
- return [sources, []] as const
36
+ return sources.map(source => {
37
+ const targets = resolveAscendingSiblings(source)
38
+ return [source, targets] as const
39
+ })
34
40
  }
35
41
 
36
- export const DirectRelationPredicate: PredicateExecutor<DeploymentRelationExpression.Direct> = {
37
- include: (expr, { model, stage }) => {
38
- const sourceIsWildcard = DeploymentElementExpression.isWildcard(expr.source)
39
- const targetIsWildcard = DeploymentElementExpression.isWildcard(expr.target)
42
+ export const DirectRelationPredicate: PredicateExecutor<RelationExpr.Direct> = {
43
+ include: ({ expr: { source, target, isBidirectional = false }, model, stage }) => {
44
+ invariant(!FqnExpr.isModelRef(source), 'Invalid source model ref in direct relation')
45
+ invariant(!FqnExpr.isModelRef(target), 'Invalid target model ref in direct relation')
46
+ const sourceIsWildcard = FqnExpr.isWildcard(source)
47
+ const targetIsWildcard = FqnExpr.isWildcard(target)
48
+
49
+ const dir = isBidirectional ? 'both' : 'directed'
40
50
 
41
- let sources, targets
51
+ let connections
42
52
 
43
53
  switch (true) {
54
+ // * -> *
44
55
  case sourceIsWildcard && targetIsWildcard: {
45
- sources = [...model.instances()]
46
- targets = sources.slice()
56
+ connections = findConnectionsWithin(model.instances()).map(c => {
57
+ stage.addImplicit(c.boundary)
58
+ return c
59
+ })
47
60
  break
48
61
  }
49
- case targetIsWildcard && isRef(expr.source): {
50
- const [
51
- resolvedSources,
52
- resolvedTargets
53
- ] = resolveIfWildcard(model, expr.source)
54
- sources = resolvedSources
55
- targets = resolvedTargets
62
+
63
+ // source -> *; source <-> *
64
+ case !sourceIsWildcard && targetIsWildcard: {
65
+ const sources = resolveElements(model, source)
66
+ const isSource = filterOutgoingConnections(sources)
67
+
68
+ let postFilter = isSource
69
+ if (isBidirectional) {
70
+ const isTarget = filterIncomingConnections(sources)
71
+ postFilter = c => isSource(c) !== isTarget(c)
72
+ }
73
+
74
+ connections = sources
75
+ .flatMap(source => {
76
+ const targets = resolveAscendingSiblings(source)
77
+ return findConnectionsBetween(source, targets, dir)
78
+ })
79
+ .filter(postFilter)
56
80
  break
57
81
  }
58
- case sourceIsWildcard && isRef(expr.target): {
59
- const [
60
- resolvedSources,
61
- resolvedTargets
62
- ] = resolveIfWildcard(model, expr.target)
63
- // Swap sources and targets
64
- sources = resolvedTargets
65
- targets = resolvedSources
82
+ // * -> target; * <-> target
83
+ case sourceIsWildcard && !targetIsWildcard: {
84
+ const targets = resolveElements(model, target)
85
+ const isTarget = filterIncomingConnections(targets)
86
+
87
+ let postFilter = isTarget
88
+ if (isBidirectional) {
89
+ const isSource = filterOutgoingConnections(targets)
90
+ postFilter = c => isSource(c) !== isTarget(c)
91
+ }
92
+
93
+ connections = targets
94
+ .flatMap(target => {
95
+ const sources = resolveAscendingSiblings(target)
96
+ return [...sources].flatMap(source => findConnection(source, target, dir))
97
+ })
98
+ .filter(postFilter)
66
99
  break
67
100
  }
68
101
  default: {
69
- invariant(isRef(expr.source), 'Type inference failed')
70
- invariant(isRef(expr.target), 'Type inference failed')
71
- sources = resolveElements(model, expr.source)
72
- targets = resolveElements(model, expr.target)
73
- }
74
- }
102
+ invariant(!sourceIsWildcard, 'Inferrence failed - source should be a deployment ref')
103
+ invariant(!targetIsWildcard, 'Inferrence failed - target should be a deployment ref')
104
+ const sources = resolveElements(model, source)
105
+ const targets = resolveElements(model, target)
75
106
 
76
- if (sources.length === 0 || targets.length === 0) {
77
- return identity()
78
- }
107
+ const isSource = filterOutgoingConnections(sources)
108
+ const isTarget = filterIncomingConnections(targets)
79
109
 
80
- const dir = expr.isBidirectional ? 'both' : 'directed'
81
- for (const source of sources) {
82
- stage.addConnections(findConnectionsBetween(source, targets, dir))
110
+ connections = pipe(
111
+ sources,
112
+ flatMap(s => findConnectionsBetween(s, targets, dir)),
113
+ filter(c => isSource(c) && isTarget(c)),
114
+ )
115
+ }
83
116
  }
84
117
 
85
- if (stage.connections.length === 0) {
86
- return identity()
87
- }
118
+ stage.addConnections(connections)
88
119
 
89
- for (const c of stage.connections) {
90
- if (c.source.isInstance() && c.target.isInstance() && c.boundary) {
91
- stage.addImplicit(c.boundary)
92
- }
93
- }
94
- if (isRef(expr.source) && isNonNullish(expr.source.selector)) {
95
- stage.addImplicit(model.element(expr.source.ref))
120
+ if (FqnExpr.isDeploymentRef(source) && isNonNullish(source.selector)) {
121
+ stage.addImplicit(model.element(source.ref.deployment))
96
122
  }
97
- if (isRef(expr.target) && isNonNullish(expr.target.selector)) {
98
- stage.addImplicit(model.element(expr.target.ref))
123
+ if (FqnExpr.isDeploymentRef(target) && isNonNullish(target.selector)) {
124
+ stage.addImplicit(model.element(target.ref.deployment))
99
125
  }
100
126
 
101
- return stage.patch()
127
+ return stage
102
128
  },
103
- exclude: (expr, { memory, stage }) => {
129
+ exclude: ({ expr, model, memory, stage }) => {
104
130
  // * -> *
105
131
  // Exclude all connections
106
132
  if (
107
- DeploymentElementExpression.isWildcard(expr.source)
108
- && DeploymentElementExpression.isWildcard(expr.target)
133
+ FqnExpr.isWildcard(expr.source)
134
+ && FqnExpr.isWildcard(expr.target)
109
135
  ) {
110
136
  stage.excludeConnections(memory.connections)
111
- return stage.patch()
137
+ return stage
138
+ }
139
+
140
+ if (FqnExpr.isModelRef(expr.source) && FqnExpr.isModelRef(expr.target)) {
141
+ const toExclude = resolveRelationsBetweenModelElements({
142
+ source: expr.source,
143
+ target: expr.target,
144
+ expr,
145
+ model,
146
+ })
147
+ return excludeModelRelations(toExclude, { stage, memory })
148
+ }
149
+
150
+ if (FqnExpr.isModelRef(expr.source)) {
151
+ if (FqnExpr.isWildcard(expr.target)) {
152
+ const toExclude = resolveAllOutgoingRelations(model, expr.source)
153
+ return excludeModelRelations(toExclude, { stage, memory })
154
+ }
155
+ const allOutgoing = resolveAllOutgoingRelations(model, expr.source)
156
+ const isTarget = deploymentExpressionToPredicate(expr.target)
157
+ return excludeModelRelations(
158
+ allOutgoing,
159
+ { stage, memory },
160
+ c => isTarget(c.target),
161
+ )
162
+ }
163
+
164
+ if (FqnExpr.isModelRef(expr.target)) {
165
+ const toExclude = resolveAllImcomingRelations(model, expr.target)
166
+ const isSource = deploymentExpressionToPredicate(expr.source)
167
+ return excludeModelRelations(
168
+ toExclude,
169
+ { stage, memory },
170
+ c => isSource(c.source),
171
+ )
112
172
  }
113
173
 
114
174
  const isSource = deploymentExpressionToPredicate(expr.source)
115
175
  const isTarget = deploymentExpressionToPredicate(expr.target)
116
176
 
117
177
  const satisfies = (connection: DeploymentConnectionModel) => {
118
- return isSource(connection.source) && isTarget(connection.target)
178
+ return (isSource(connection.source) && isTarget(connection.target))
119
179
  || (expr.isBidirectional && isSource(connection.target) && isTarget(connection.source))
120
180
  }
121
181
 
122
182
  const toExclude = memory.connections.filter(satisfies)
123
- if (toExclude.length === 0) {
124
- return identity()
125
- }
126
183
 
127
184
  stage.excludeConnections(toExclude)
128
- return stage.patch()
185
+ return stage
186
+ },
187
+ }
188
+
189
+ export function excludeModelRelations(
190
+ relationsToExclude: ReadonlySet<RelationshipModel<AnyAux>>,
191
+ { stage, memory }: Pick<ExcludePredicateCtx, 'stage' | 'memory'>,
192
+ // Optional filter to scope the connections to exclude
193
+ filterConnections: (c: DeploymentConnectionModel) => boolean = () => true,
194
+ ): StageExclude {
195
+ if (relationsToExclude.size === 0) {
196
+ return stage
197
+ }
198
+ const toExclude = pipe(
199
+ memory.connections,
200
+ // Find connections that have at least one relation in common with the excluded relations
201
+ filter(c => filterConnections(c) && hasIntersection(c.relations.model, relationsToExclude)),
202
+ map(c =>
203
+ c.update({
204
+ deployment: null,
205
+ model: intersection(c.relations.model, relationsToExclude),
206
+ })
207
+ ),
208
+ )
209
+ if (toExclude.length === 0) {
210
+ return stage
211
+ }
212
+ return stage.excludeConnections(toExclude)
213
+ }
214
+
215
+ function resolveRelationsBetweenModelElements({
216
+ source,
217
+ target,
218
+ expr,
219
+ model,
220
+ }: {
221
+ source: FqnExpr.ModelRef
222
+ target: FqnExpr.ModelRef
223
+ expr: RelationExpr.Direct
224
+ model: LikeC4DeploymentModel
225
+ }) {
226
+ const sources = resolveModelElements(model, source)
227
+ const targets = resolveModelElements(model, target)
228
+
229
+ const dir = expr.isBidirectional ? 'both' : 'directed'
230
+
231
+ const modelConnections = [] as ConnectionModel[]
232
+ for (const source of sources) {
233
+ modelConnections.push(...findModelConnectionsBetween(source, targets, dir))
129
234
  }
235
+
236
+ return new Set(modelConnections.flatMap(c => [...c.relations]))
130
237
  }
@@ -1,43 +1,61 @@
1
- import { identity } from 'remeda'
2
- import type { DeploymentConnectionModel } from '../../../model/connection/deployment'
1
+ import { invariant } from '../../../errors'
3
2
  import { findConnectionsBetween } from '../../../model/connection/deployment'
4
- import { DeploymentElementExpression, type DeploymentRelationExpression } from '../../../types/deployments'
3
+ import type { RelationshipModel } from '../../../model/RelationModel'
4
+ import type { AnyAux } from '../../../model/types'
5
+ import { FqnExpr, type RelationExpr } from '../../../types/expression-v2'
6
+ import { union } from '../../../utils/set'
5
7
  import type { PredicateExecutor } from '../_types'
6
- import { deploymentExpressionToPredicate, resolveElements } from '../utils'
7
- import { resolveAscendingSiblings } from './relation-direct'
8
+ import { resolveElements, resolveModelElements } from '../utils'
9
+ import { excludeModelRelations, resolveAscendingSiblings } from './relation-direct'
10
+ import { filterIncomingConnections } from './relation-incoming'
11
+ import { filterOutgoingConnections } from './relation-outgoing'
8
12
 
9
13
  //
10
- export const InOutRelationPredicate: PredicateExecutor<DeploymentRelationExpression.InOut> = {
11
- include: (expr, { model, memory, stage }) => {
14
+ export const InOutRelationPredicate: PredicateExecutor<RelationExpr.InOut> = {
15
+ include: ({ expr, model, memory, stage }) => {
12
16
  const sources = [...memory.elements]
13
- if (DeploymentElementExpression.isWildcard(expr.inout)) {
17
+
18
+ if (FqnExpr.isWildcard(expr.inout)) {
14
19
  for (const source of sources) {
15
20
  const targets = [...resolveAscendingSiblings(source)]
16
21
  stage.addConnections(findConnectionsBetween(source, targets, 'both'))
17
22
  }
18
- return stage.patch()
23
+ return stage
19
24
  }
25
+ invariant(FqnExpr.isDeploymentRef(expr.inout), 'Only deployment refs are supported in include')
20
26
 
21
27
  const targets = resolveElements(model, expr.inout)
22
28
  for (const source of sources) {
23
29
  stage.addConnections(findConnectionsBetween(source, targets, 'both'))
24
30
  }
25
31
 
26
- return stage.patch()
32
+ return stage
27
33
  },
28
- exclude: (expr, { memory, stage }) => {
29
- const isSourceOrTarget = deploymentExpressionToPredicate(expr.inout)
30
-
31
- const satisfies = (connection: DeploymentConnectionModel) => {
32
- return isSourceOrTarget(connection.source) || isSourceOrTarget(connection.target)
34
+ exclude: ({ expr, model, memory, stage }) => {
35
+ // Exclude all connections that have model relationshps with the elements
36
+ if (FqnExpr.isModelRef(expr.inout)) {
37
+ const elements = resolveModelElements(model, expr.inout)
38
+ if (elements.length === 0) {
39
+ return stage
40
+ }
41
+ const excludedRelations = union(
42
+ new Set<RelationshipModel<AnyAux>>(),
43
+ ...elements.flatMap(e => [e.allIncoming, e.allOutgoing]),
44
+ )
45
+ return excludeModelRelations(excludedRelations, { stage, memory })
33
46
  }
34
47
 
35
- const toExclude = memory.connections.filter(satisfies)
36
- if (toExclude.length === 0) {
37
- return identity()
48
+ if (FqnExpr.isWildcard(expr.inout)) {
49
+ // non-sense
50
+ return stage
38
51
  }
39
52
 
53
+ const elements = resolveElements(model, expr.inout)
54
+ const isIncoming = filterIncomingConnections(elements)
55
+ const isOutgoing = filterOutgoingConnections(elements)
56
+
57
+ const toExclude = memory.connections.filter(c => isIncoming(c) !== isOutgoing(c))
40
58
  stage.excludeConnections(toExclude)
41
- return stage.patch()
42
- }
59
+ return stage
60
+ },
43
61
  }
@@ -1,16 +1,21 @@
1
- import { identity } from 'remeda'
2
- import { findConnection, findConnectionsBetween } from '../../../model/connection/deployment'
3
- import type { DeploymentConnectionModel } from '../../../model/connection/deployment'
4
- import { DeploymentElementExpression, type DeploymentRelationExpression } from '../../../types/deployments'
5
- import type { PredicateExecutor } from '../_types'
6
- import { deploymentExpressionToPredicate, resolveElements } from '../utils'
7
- import { resolveAscendingSiblings } from './relation-direct'
1
+ import { anyPass } from 'remeda'
2
+ import { invariant } from '../../../errors'
3
+ import { findConnectionsBetween } from '../../../model/connection/deployment'
4
+ import type { LikeC4DeploymentModel } from '../../../model/DeploymentModel'
5
+ import type { RelationshipModel } from '../../../model/RelationModel'
6
+ import type { AnyAux } from '../../../model/types'
7
+ import { FqnExpr, type RelationExpr } from '../../../types'
8
+ import { isAncestor } from '../../../utils/fqn'
9
+ import type { Connection, Elem, PredicateExecutor } from '../_types'
10
+ import { resolveElements, resolveModelElements } from '../utils'
11
+ import { excludeModelRelations, resolveAscendingSiblings } from './relation-direct'
8
12
 
9
13
  // from visible element incoming to this
10
- export const IncomingRelationPredicate: PredicateExecutor<DeploymentRelationExpression.Incoming> = {
11
- include: (expr, { model, memory, stage }) => {
14
+ export const IncomingRelationPredicate: PredicateExecutor<RelationExpr.Incoming> = {
15
+ include: ({ expr, model, memory, stage }) => {
12
16
  const sources = [...memory.elements]
13
- if (DeploymentElementExpression.isWildcard(expr.incoming)) {
17
+
18
+ if (FqnExpr.isWildcard(expr.incoming)) {
14
19
  for (const source of sources) {
15
20
  if (source.allOutgoing.isEmpty) {
16
21
  continue
@@ -18,29 +23,73 @@ export const IncomingRelationPredicate: PredicateExecutor<DeploymentRelationExpr
18
23
  const targets = [...resolveAscendingSiblings(source)]
19
24
  stage.addConnections(findConnectionsBetween(source, targets, 'directed'))
20
25
  }
21
- return stage.patch()
26
+ return stage
22
27
  }
28
+ invariant(FqnExpr.isDeploymentRef(expr.incoming), 'Only deployment refs are supported in include')
23
29
 
24
30
  const targets = resolveElements(model, expr.incoming)
25
31
  for (const source of sources) {
26
32
  stage.addConnections(findConnectionsBetween(source, targets, 'directed'))
27
33
  }
28
34
 
29
- return stage.patch()
35
+ return stage
30
36
  },
31
- exclude: (expr, { memory, stage }) => {
32
- const isTarget = deploymentExpressionToPredicate(expr.incoming)
33
-
34
- const satisfies = (connection: DeploymentConnectionModel) => {
35
- return isTarget(connection.target)
37
+ exclude: ({ expr, model, memory, stage }) => {
38
+ // Exclude all connections that have model relationshps with the elements
39
+ if (FqnExpr.isModelRef(expr.incoming)) {
40
+ const excludedRelations = resolveAllImcomingRelations(model, expr.incoming)
41
+ return excludeModelRelations(excludedRelations, { stage, memory })
36
42
  }
37
-
38
- const toExclude = memory.connections.filter(satisfies)
39
- if (toExclude.length === 0) {
40
- return identity()
43
+ if (FqnExpr.isWildcard(expr.incoming)) {
44
+ // non-sense
45
+ return stage
41
46
  }
42
47
 
48
+ const isIncoming = filterIncomingConnections(resolveElements(model, expr.incoming))
49
+ const toExclude = memory.connections.filter(isIncoming)
43
50
  stage.excludeConnections(toExclude)
44
- return stage.patch()
45
- }
51
+ return stage
52
+ },
53
+ }
54
+
55
+ export function filterIncomingConnections(
56
+ targets: Elem[],
57
+ ): (connection: Connection) => boolean {
58
+ return anyPass(
59
+ targets.map(target => {
60
+ const satisfies = (el: Elem) => el === target || isAncestor(target, el)
61
+ return (connection: Connection) => {
62
+ return !satisfies(connection.source) && satisfies(connection.target)
63
+ }
64
+ }),
65
+ )
66
+ }
67
+ // model: LikeC4DeploymentModel,
68
+ // model: LikeC4DeploymentModel,
69
+ // expr: FqnExpr.DeploymentRef
70
+ // ): (connection: Connection) => boolean {
71
+ // if (FqnExpr.isWildcard(expr)) {
72
+ // return () => true
73
+ // }
74
+ // if (isNullish(expr.selector)) {
75
+ // // -> element
76
+ // const target = model.element(expr.ref.deployment)
77
+ // const isInside = (el: Elem) => el === target || isAncestor(target, el)
78
+ // return (connection) => {
79
+ // return !isInside(connection.source) && isInside(connection.target)
80
+ // }
81
+ // }
82
+
83
+ // const isTarget = deploymentExpressionToPredicate(expr)
84
+ // return (connection) => {
85
+ // return isTarget(connection.target)
86
+ // }
87
+ // }
88
+
89
+ export function resolveAllImcomingRelations(
90
+ model: LikeC4DeploymentModel,
91
+ moodelRef: FqnExpr.ModelRef,
92
+ ): Set<RelationshipModel<AnyAux>> {
93
+ const targets = resolveModelElements(model, moodelRef)
94
+ return new Set(targets.flatMap(e => [...e.allIncoming]))
46
95
  }