@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
@@ -1,18 +1,21 @@
1
- import { identity } from 'remeda'
1
+ import { anyPass } from 'remeda'
2
+ import { invariant } from '../../../errors'
3
+ import type { LikeC4DeploymentModel } from '../../../model'
2
4
  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'
5
+ import type { RelationshipModel } from '../../../model/RelationModel'
6
+ import { FqnExpr, type RelationExpr } from '../../../types'
7
+ import { isAncestor } from '../../../utils'
8
+ import type { Connection, Elem, PredicateExecutor } from '../_types'
9
+ import { resolveElements, resolveModelElements } from '../utils'
10
+ import { excludeModelRelations, resolveAscendingSiblings } from './relation-direct'
8
11
 
9
- export const OutgoingRelationPredicate: PredicateExecutor<DeploymentRelationExpression.Outgoing> = {
10
- include: (expr, { model, memory, stage }) => {
12
+ export const OutgoingRelationPredicate: PredicateExecutor<RelationExpr.Outgoing> = {
13
+ include: ({ expr, model, memory, stage }) => {
11
14
  const targets = [...memory.elements]
12
15
 
13
16
  // * -> (to visible elements)
14
17
  // outgoing from wildcard to visible element
15
- if (DeploymentElementExpression.isWildcard(expr.outgoing)) {
18
+ if (FqnExpr.isWildcard(expr.outgoing)) {
16
19
  for (const target of targets) {
17
20
  if (target.allIncoming.isEmpty) {
18
21
  continue
@@ -21,29 +24,65 @@ export const OutgoingRelationPredicate: PredicateExecutor<DeploymentRelationExpr
21
24
  stage.addConnections(findConnection(source, target, 'directed'))
22
25
  }
23
26
  }
24
- return stage.patch()
27
+ return stage
25
28
  }
29
+ invariant(FqnExpr.isDeploymentRef(expr.outgoing), 'Only deployment refs are supported in include')
26
30
 
27
31
  const sources = resolveElements(model, expr.outgoing)
28
32
  for (const source of sources) {
29
33
  stage.addConnections(findConnectionsBetween(source, targets, 'directed'))
30
34
  }
31
35
 
32
- return stage.patch()
36
+ return stage
33
37
  },
34
- exclude: (expr, { memory, stage }) => {
35
- const isSource = deploymentExpressionToPredicate(expr.outgoing)
36
-
37
- const satisfies = (connection: DeploymentConnectionModel) => {
38
- return isSource(connection.source)
38
+ exclude: ({ expr, model, memory, stage }) => {
39
+ // Exclude all connections that have model relationshps with the elements
40
+ if (FqnExpr.isModelRef(expr.outgoing)) {
41
+ const excludedRelations = resolveAllOutgoingRelations(model, expr.outgoing)
42
+ return excludeModelRelations(excludedRelations, { stage, memory })
39
43
  }
40
-
41
- const toExclude = memory.connections.filter(satisfies)
42
- if (toExclude.length === 0) {
43
- return identity()
44
+ if (FqnExpr.isWildcard(expr.outgoing)) {
45
+ // non-sense
46
+ return stage
44
47
  }
45
48
 
49
+ const isOutgoing = filterOutgoingConnections(resolveElements(model, expr.outgoing))
50
+ const toExclude = memory.connections.filter(isOutgoing)
46
51
  stage.excludeConnections(toExclude)
47
- return stage.patch()
48
- }
52
+ return stage
53
+ },
54
+ }
55
+
56
+ export function filterOutgoingConnections(
57
+ sources: Elem[],
58
+ ): (connection: Connection) => boolean {
59
+ return anyPass(
60
+ sources.map(source => {
61
+ const satisfies = (el: Elem) => el === source || isAncestor(source, el)
62
+ return (connection: Connection) => {
63
+ return satisfies(connection.source) && !satisfies(connection.target)
64
+ }
65
+ }),
66
+ )
67
+ // if (FqnExpr.isDeploymentRef(expr) && isNullish(expr.selector)) {
68
+ // // element ->
69
+ // const source = model.element(expr.ref.deployment)
70
+ // const isInside = (el: Elem) => el === source || isAncestor(source, el)
71
+ // return (connection: Connection) => {
72
+ // return isInside(connection.source) && !isInside(connection.target)
73
+ // }
74
+ // } else {
75
+ // const isSource = deploymentExpressionToPredicate(expr)
76
+ // return (connection: Connection) => {
77
+ // return isSource(connection.source)
78
+ // }
79
+ // }
80
+ }
81
+
82
+ export function resolveAllOutgoingRelations(
83
+ model: LikeC4DeploymentModel,
84
+ moodelRef: FqnExpr.ModelRef,
85
+ ): Set<RelationshipModel> {
86
+ const targets = resolveModelElements(model, moodelRef)
87
+ return new Set(targets.flatMap(e => [...e.allOutgoing]))
49
88
  }
@@ -1,29 +1,29 @@
1
1
  import { findConnectionsWithin } from '../../../model/connection/deployment'
2
- import type { DeploymentElementExpression } from '../../../types/deployments'
3
- import type { Elem, PredicateExecutor } from '../_types'
4
- import { MutableMemory } from '../Memory'
2
+ import type { FqnExpr } from '../../../types'
3
+ import { type Elem, type PredicateExecutor } from '../_types'
5
4
 
6
- export const WildcardPredicate: PredicateExecutor<DeploymentElementExpression.Wildcard> = {
7
- include: (_, { model, stage }) => {
5
+ export const WildcardPredicate: PredicateExecutor<FqnExpr.Wildcard> = {
6
+ include: ({ model, stage }) => {
8
7
  const children = [] as Elem[]
9
8
 
10
9
  const rootElements = [...model.roots()].map(root => {
11
- const onlyOneInstance = root.onlyOneInstance()
12
- if (onlyOneInstance) {
13
- children.push(onlyOneInstance)
14
- return onlyOneInstance
10
+ if (!root.onlyOneInstance()) {
11
+ children.push(...root.children())
15
12
  }
16
- children.push(...root.children())
17
13
  return root
18
14
  })
19
15
 
20
16
  stage.addExplicit(rootElements)
21
17
  if (children.length > 1) {
22
- stage.addConnections(findConnectionsWithin(children))
18
+ stage.addConnections(findConnectionsWithin([
19
+ ...rootElements,
20
+ ...children,
21
+ ]))
23
22
  }
24
- return stage.patch()
23
+ return stage
24
+ },
25
+ exclude: ({ stage, memory }) => {
26
+ stage.exclude(memory.elements)
27
+ return stage
25
28
  },
26
- exclude: () => {
27
- return () => MutableMemory.empty()
28
- }
29
29
  }
@@ -0,0 +1,5 @@
1
+ import { AbstractStageExclude } from '../../memory'
2
+ import { type Ctx } from '../memory/memory'
3
+
4
+ export class StageExclude extends AbstractStageExclude<Ctx> {
5
+ }
@@ -0,0 +1,245 @@
1
+ import DefaultMap from 'mnemonist/default-map'
2
+ import { forEach, only, pipe } from 'remeda'
3
+ import { differenceConnections } from '../../../model/connection'
4
+ import type { RelationshipModel } from '../../../model/RelationModel'
5
+ import { isAncestor, sortByFqnHierarchically } from '../../../utils/fqn'
6
+ import { ifilter, isome, toArray } from '../../../utils/iterable'
7
+ import { difference, union } from '../../../utils/set'
8
+ import { treeFromMemoryState } from '../../memory'
9
+ import { cleanCrossBoundary, cleanRedundantRelationships } from '../clean-connections'
10
+ import type { Ctx, Memory } from '../memory'
11
+
12
+ type Elem = Ctx['Element']
13
+ /**
14
+ * This patch:
15
+ * 1. Keeps connections between leafs or having direct deployment relations
16
+ * 2. Removes cross-boundary model relations, that already exist inside boundaries
17
+ * (e.g. prefer relations inside same deployment node over relations between nodes)
18
+ * 3. Removes implicit connections between elements, if their descendants have same connection
19
+ */
20
+ export class StageFinal {
21
+ static for(memory: Memory) {
22
+ return new StageFinal(memory)
23
+ }
24
+
25
+ private constructor(
26
+ protected readonly memory: Memory,
27
+ ) {
28
+ }
29
+
30
+ public step1CleanConnections(memory: Memory): Memory {
31
+ if (memory.connections.length < 2) {
32
+ return memory
33
+ }
34
+
35
+ // const leafs = new Set<Elem>()
36
+
37
+ // for (const element of memory.final) {
38
+ // // Instance is always leaf
39
+ // if (element.isInstance()) {
40
+ // leafs.add(element)
41
+ // continue
42
+ // }
43
+ // // Check if element is ancestor of any element in elements
44
+ // let isLeaf = true
45
+ // for (const el of memory.final) {
46
+ // if (isAncestor(element.id, el.id)) {
47
+ // isLeaf = false
48
+ // break
49
+ // }
50
+ // }
51
+ // if (isLeaf) {
52
+ // leafs.add(element)
53
+ // }
54
+ // }
55
+
56
+ const connections = pipe(
57
+ memory.connections,
58
+ // Keep connections
59
+ // - between leafs
60
+ // - has direct deployment relation
61
+ // filter(c => {
62
+ // return (leafs.has(c.source) && leafs.has(c.target)) || c.hasDirectDeploymentRelation()
63
+ // }),
64
+ cleanCrossBoundary,
65
+ cleanRedundantRelationships,
66
+ )
67
+
68
+ const connectionsToExclude = differenceConnections(
69
+ memory.connections,
70
+ connections,
71
+ )
72
+ if (connectionsToExclude.length === 0) {
73
+ return memory
74
+ }
75
+ const stage = memory.stageExclude({} as any)
76
+ stage.excludeConnections(connectionsToExclude, true)
77
+ return stage.commit()
78
+ }
79
+
80
+ public step2ProcessImplicits(memory: Memory): Memory {
81
+ const implicits = union(this.memory.elements, memory.elements)
82
+ const final = union(memory.final, this.memory.explicits)
83
+
84
+ // Find connections based on same relation but different boundaries
85
+ const groupedByRelation = new DefaultMap<RelationshipModel, Set<Elem>>(() => new Set())
86
+ for (const conn of memory.connections) {
87
+ if (conn.boundary) {
88
+ for (const relation of conn.relations.model) {
89
+ groupedByRelation.get(relation).add(conn.boundary)
90
+ }
91
+ }
92
+ }
93
+ for (const [_, boundaries] of groupedByRelation) {
94
+ if (boundaries.size < 2) {
95
+ continue
96
+ }
97
+ for (const boundary of boundaries) {
98
+ if (implicits.delete(boundary)) {
99
+ final.add(boundary)
100
+ }
101
+ }
102
+ }
103
+
104
+ const snapshot = new Set(final)
105
+ const isFinalOrHasIncludedDescendant = (el: Elem) => {
106
+ for (const final of snapshot) {
107
+ if (el === final || isAncestor(el, final)) {
108
+ return true
109
+ }
110
+ }
111
+ return false
112
+ }
113
+
114
+ // Pick from implicit elements
115
+ pipe(
116
+ difference(implicits, snapshot),
117
+ ifilter(e => e.isDeploymentNode()),
118
+ toArray(),
119
+ sortByFqnHierarchically,
120
+ forEach((el) => {
121
+ // If element has more 2 or more children included
122
+ // It can "box" around
123
+ const childrensToWrap = [...el.children()].filter(isFinalOrHasIncludedDescendant).length
124
+ if (childrensToWrap >= 2) {
125
+ final.add(el)
126
+ return
127
+ }
128
+ // If Element has only one child included
129
+ // and has "similar" sibling with included descendant
130
+ if (childrensToWrap === 1 && isome(el.siblings(), isFinalOrHasIncludedDescendant)) {
131
+ final.add(el)
132
+ }
133
+ }),
134
+ )
135
+
136
+ return memory.update({ final })
137
+ }
138
+
139
+ public step3ProcessBoundaries(memory: Memory): Memory {
140
+ const boundaries = new Set<Elem>()
141
+ for (const conn of memory.connections) {
142
+ if (conn.boundary) {
143
+ boundaries.add(conn.boundary)
144
+ }
145
+ }
146
+ const tree = treeFromMemoryState<Ctx>(memory, 'final')
147
+ const stage = memory.stageExclude({} as any)
148
+
149
+ const isRemovable = (el: Elem) =>
150
+ !(
151
+ boundaries.has(el)
152
+ || memory.explicits.has(el)
153
+ || tree.hasInOut(el)
154
+ || tree.root.has(el)
155
+ )
156
+
157
+ const singleRoot = only([...tree.root])
158
+ if (singleRoot && !memory.explicits.has(singleRoot)) {
159
+ stage.exclude(singleRoot)
160
+ }
161
+
162
+ for (const el of memory.final) {
163
+ const singleChild = only(tree.children(el))
164
+ if (singleChild && !tree.hasInOut(singleChild) && isRemovable(el)) {
165
+ stage.exclude(el)
166
+ }
167
+ }
168
+
169
+ if (stage.isDirty()) {
170
+ return stage.commit()
171
+ }
172
+ return memory
173
+ }
174
+
175
+ // TODO: Lot of corner cases to cover, skip for now
176
+ // public step3FlatNodes(memory: Memory): Memory {
177
+ // // final implicits
178
+ // const explicits = new Set<Elem>([
179
+ // ...memory.explicits,
180
+ // ...memory.connections.flatMap(c => [c.source, c.target]),
181
+ // ])
182
+ // const sorted = sortParentsFirst(toArray(memory.final))
183
+
184
+ // const toplevel = new Set<Elem>(sorted)
185
+ // const children = sorted.reduce((acc, el, index, all) => {
186
+ // acc.set(
187
+ // el,
188
+ // new Set(
189
+ // all
190
+ // .slice(index + 1)
191
+ // .filter(e => isAncestor(el, e))
192
+ // .reduce((acc, el) => {
193
+ // if (!acc.some(e => isAncestor(e, el))) {
194
+ // toplevel.delete(el)
195
+ // acc.push(el)
196
+ // }
197
+ // return acc
198
+ // }, [] as Elem[]),
199
+ // ),
200
+ // )
201
+ // return acc
202
+ // }, new DefaultMap<Elem, Set<Elem>>(() => new Set()))
203
+
204
+ // const state = memory.mutableState()
205
+
206
+ // function flattenNode(node: Elem) {
207
+ // const _children = [...children.get(node)]
208
+ // if (_children.length > 1) {
209
+ // for (const child of _children) {
210
+ // flattenNode(child)
211
+ // }
212
+ // return !explicits.has(node)
213
+ // }
214
+ // if (hasAtLeast(_children, 1)) {
215
+ // if (flattenNode(_children[0])) {
216
+ // state.final.delete(_children[0])
217
+ // }
218
+ // }
219
+ // return !explicits.has(node)
220
+ // }
221
+
222
+ // const root = [...toplevel]
223
+ // for (const node of root) {
224
+ // flattenNode(node)
225
+ // if (!explicits.has(node) && children.get(node).size === 1) {
226
+ // state.final.delete(node)
227
+ // }
228
+ // }
229
+ // if (root.length === 1 && !explicits.has(root[0]!)) {
230
+ // state.final.delete(root[0]!)
231
+ // }
232
+
233
+ // return memory.update(state)
234
+ // }
235
+
236
+ public commit(): Memory {
237
+ // return []
238
+ const step1 = this.step1CleanConnections(this.memory)
239
+ const step2 = this.step2ProcessImplicits(step1)
240
+ return this.step3ProcessBoundaries(step2)
241
+ // return step2memory
242
+ // const step3m?emory = this.step3FlatNodes(step2memory)
243
+ // return step3memory
244
+ }
245
+ }
@@ -0,0 +1,95 @@
1
+ import { dropWhile, forEach, pipe, take, zip } from 'remeda'
2
+ import { findConnection, findConnectionsBetween } from '../../../model/connection/deployment'
3
+ import { isIterable } from '../../../utils'
4
+ import { toArray } from '../../../utils/iterable'
5
+ import { type CtxConnection, type CtxElement, AbstractStageInclude } from '../../memory'
6
+ import { cleanCrossBoundary, cleanRedundantRelationships } from '../clean-connections'
7
+ import { type Ctx } from '../memory/memory'
8
+
9
+ type Elem = CtxElement<Ctx>
10
+ type Connection = CtxConnection<Ctx>
11
+
12
+ export class StageInclude extends AbstractStageInclude<Ctx> {
13
+ /**
14
+ * Connects elements with existing ones in the memory
15
+ */
16
+ public override connectWithExisting(
17
+ elements: Elem | Iterable<Elem>,
18
+ direction: 'in' | 'out' | 'both' = 'both',
19
+ ): boolean {
20
+ const before = this._connections.length
21
+ const hasChanged = () => this._connections.length > before
22
+ if (!isIterable(elements)) {
23
+ if (direction === 'in' || direction === 'both') {
24
+ for (const el of this.memory.elements) {
25
+ this.addConnections(
26
+ findConnection(el, elements, 'directed'),
27
+ )
28
+ }
29
+ }
30
+ if (direction === 'out' || direction === 'both') {
31
+ this.addConnections(
32
+ findConnectionsBetween(elements, this.memory.elements, 'directed'),
33
+ )
34
+ }
35
+ return hasChanged()
36
+ }
37
+
38
+ const targets = [...elements]
39
+ if (direction === 'in' || direction === 'both') {
40
+ for (const el of this.memory.elements) {
41
+ this.addConnections(
42
+ findConnectionsBetween(el, targets, 'directed'),
43
+ )
44
+ }
45
+ }
46
+ if (direction === 'out' || direction === 'both') {
47
+ for (const el of targets) {
48
+ this.addConnections(
49
+ findConnectionsBetween(el, this.memory.elements, 'directed'),
50
+ )
51
+ }
52
+ }
53
+ return hasChanged()
54
+ }
55
+
56
+ protected override processConnections(connections: Connection[]) {
57
+ const clean = pipe(
58
+ connections,
59
+ cleanCrossBoundary,
60
+ cleanRedundantRelationships,
61
+ )
62
+
63
+ pipe(
64
+ clean,
65
+ // Process only connection from this stage
66
+ // filter(c => this._connections.some(c2 => c2.id === c.id)),
67
+ forEach(({ source, target, boundary }) => {
68
+ pipe(
69
+ zip(
70
+ [...toArray(source.ancestors()).reverse(), source],
71
+ [...toArray(target.ancestors()).reverse(), target],
72
+ ),
73
+ // Filter out common ancestors
74
+ dropWhile(([sourceAncestor, targetAncestor]) => sourceAncestor === targetAncestor),
75
+ take(1),
76
+ forEach(([sourceAncestor, targetAncestor]) => {
77
+ if (source === sourceAncestor && target === targetAncestor) {
78
+ this.addImplicit(boundary)
79
+ return
80
+ }
81
+ if (sourceAncestor !== source && sourceAncestor.isDeploymentNode() && !sourceAncestor.onlyOneInstance()) {
82
+ // state.final.add(source)
83
+ this.addImplicit(sourceAncestor)
84
+ }
85
+ if (targetAncestor !== target && targetAncestor.isDeploymentNode() && !targetAncestor.onlyOneInstance()) {
86
+ // state.final.add(source)
87
+ this.addImplicit(targetAncestor)
88
+ }
89
+ }),
90
+ )
91
+ }),
92
+ )
93
+ return clean
94
+ }
95
+ }