@likec4/core 1.18.0 → 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 (176) 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.-BdzdI2C.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 +404 -0
  30. package/dist/utils/index.d.ts +404 -0
  31. package/dist/utils/index.js +330 -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 +74 -9
  148. package/src/utils/iterable/_types.ts +5 -0
  149. package/src/utils/iterable/filter.ts +55 -0
  150. package/src/utils/iterable/flat.ts +17 -0
  151. package/src/utils/iterable/index.ts +12 -0
  152. package/src/utils/iterable/map.ts +40 -0
  153. package/src/utils/iterable/reduce.ts +24 -0
  154. package/src/utils/iterable/some.ts +36 -0
  155. package/src/utils/iterable/to.ts +19 -0
  156. package/src/utils/iterable/unique.ts +29 -0
  157. package/src/utils/relations.ts +3 -64
  158. package/src/utils/set.ts +11 -9
  159. package/src/utils/string-hash.ts +1 -1
  160. package/dist/compute-view/index.mjs +0 -79
  161. package/dist/model/index.mjs +0 -2
  162. package/dist/shared/core.BBgiAAv1.mjs +0 -144
  163. package/dist/shared/core.BPyJvuMU.mjs +0 -2420
  164. package/dist/shared/core.BQ4AbfvB.mjs +0 -9228
  165. package/dist/shared/core.k12s_keD.mjs +0 -203
  166. package/dist/types/index.mjs +0 -21
  167. package/src/builder/Builder.test-d.ts +0 -124
  168. package/src/builder/Builder.view.ts +0 -543
  169. package/src/compute-view/LikeC4ModelGraph.ts +0 -330
  170. package/src/compute-view/deployment-view/Memory.ts +0 -108
  171. package/src/compute-view/deployment-view/Stage.ts +0 -145
  172. package/src/compute-view/deployment-view/predicates/deployment-ref.ts +0 -146
  173. package/src/compute-view/element-view/predicates.ts +0 -517
  174. package/src/compute-view/utils/sortNodes.ts +0 -101
  175. package/src/model/connection/ConnectionModel.ts +0 -94
  176. package/src/model/connection/DeploymentConnectionModel.ts +0 -99
@@ -0,0 +1,88 @@
1
+ import { filter } from 'remeda'
2
+ import { findConnectionsBetween, findConnectionsWithin } from '../../../model/connection/model'
3
+ import type { WildcardExpr } from '../../../types/expression'
4
+ import { ifilter, toArray } from '../../../utils/iterable'
5
+ import { toSet } from '../../../utils/iterable/to'
6
+ import { type PredicateExecutor, Memory } from '../_types'
7
+ import { NoWhere } from '../utils'
8
+
9
+ export const WildcardPredicate: PredicateExecutor<WildcardExpr> = {
10
+ include: ({ scope, model, stage, memory, where }) => {
11
+ if (!scope) {
12
+ const rootElements = [...model.roots()].filter(where)
13
+ if (rootElements.length === 0) {
14
+ return
15
+ }
16
+ stage.addExplicit(rootElements)
17
+ stage.addConnections(findConnectionsWithin(rootElements))
18
+
19
+ stage.connectWithExisting(rootElements)
20
+
21
+ return stage
22
+ }
23
+ const root = where(scope) ? scope : null
24
+
25
+ const children = toArray(ifilter(scope.children(), where))
26
+ const hasChildren = children.length > 0
27
+ if (!hasChildren) {
28
+ if (!root) {
29
+ return stage
30
+ } else {
31
+ // Any edges with siblings?
32
+ const edgesWithSiblings = findConnectionsBetween(root, root.siblings())
33
+ if (edgesWithSiblings.length === 0) {
34
+ // If no edges with siblings, i.e. root is orphan
35
+ // Lets add parent for better view
36
+ const parent = root.parent
37
+ if (parent && where(parent)) {
38
+ stage.addExplicit(parent)
39
+ }
40
+ }
41
+ children.push(root)
42
+ }
43
+ }
44
+
45
+ if (root) {
46
+ stage.addExplicit(root)
47
+ }
48
+
49
+ const neighbours = toSet([
50
+ ...memory.elements,
51
+ ...scope.descendingSiblings(),
52
+ ])
53
+
54
+ // Add incoming connections
55
+ for (const neighbour of neighbours) {
56
+ stage.addConnections(findConnectionsBetween(neighbour, children, 'directed'))
57
+ }
58
+
59
+ // connection between children
60
+ if (hasChildren) {
61
+ stage.addConnections(findConnectionsWithin(children))
62
+ stage.addExplicit(children)
63
+ }
64
+
65
+ // Add outgoing connections
66
+ for (const child of children) {
67
+ stage.addConnections(findConnectionsBetween(child, neighbours, 'directed'))
68
+ }
69
+
70
+ return stage
71
+ },
72
+ exclude: ({ scope, memory, stage, where }) => {
73
+ if (where !== NoWhere) {
74
+ stage.exclude(
75
+ filter(
76
+ [...memory.elements],
77
+ where,
78
+ ),
79
+ )
80
+ return stage
81
+ }
82
+ if (scope) {
83
+ stage.exclude([scope, ...scope.descendants()])
84
+ return stage
85
+ }
86
+ return Memory.empty(memory.scope).stageExclude(stage.expression)
87
+ },
88
+ }
@@ -0,0 +1,59 @@
1
+ import { hasAtLeast } from 'remeda'
2
+ import { invariant } from '../../errors'
3
+ import { type ComputedEdge, type ComputedNode, type Fqn } from '../../types'
4
+ import { buildComputedNodes, type ComputedNodeSource } from '../utils/buildComputedNodes'
5
+ import { mergePropsFromRelationships } from '../utils/merge-props-from-relationships'
6
+ import type { Connection, Elem, Memory } from './_types'
7
+
8
+ export const NoWhere = () => true
9
+ export const NoFilter = <T>(x: T[] | readonly T[]): T[] => x as T[]
10
+
11
+ export function toNodeSource(el: Elem): ComputedNodeSource {
12
+ return {
13
+ ...el.$element,
14
+ modelRef: 1
15
+ }
16
+ }
17
+
18
+ export function toComputedEdges(
19
+ connections: ReadonlyArray<Connection>
20
+ ): ComputedEdge[] {
21
+ return connections.reduce((acc, e) => {
22
+ // const modelRelations = []
23
+ // const deploymentRelations = []
24
+ const relations = [
25
+ ...e.relations
26
+ ]
27
+ invariant(hasAtLeast(relations, 1), 'Edge must have at least one relation')
28
+
29
+ const source = e.source.id
30
+ const target = e.target.id
31
+
32
+ const {
33
+ title,
34
+ ...props
35
+ } = mergePropsFromRelationships(
36
+ relations.map(r => r.$relationship),
37
+ relations.find(r => r.source.id === source && r.target.id === target)?.$relationship
38
+ )
39
+
40
+ const edge: ComputedEdge = {
41
+ id: e.id,
42
+ parent: e.boundary?.id ?? null,
43
+ source,
44
+ target,
45
+ label: title ?? null,
46
+ relations: relations.map((r) => r.id),
47
+ ...props
48
+ }
49
+
50
+ acc.push(edge)
51
+ return acc
52
+ }, [] as ComputedEdge[])
53
+ }
54
+
55
+ export function buildNodes(memory: Memory): ReadonlyMap<Fqn, ComputedNode> {
56
+ // typecast to MutableMemory
57
+ // invariant(memory instanceof MutableMemory, 'Expected MutableMemory')
58
+ return buildComputedNodes([...memory.final].map(toNodeSource), memory.groups)
59
+ }
@@ -1,5 +1,4 @@
1
- export { computeViews, mkComputeView } from './compute-view'
2
- export { LikeC4ModelGraph } from './LikeC4ModelGraph'
1
+ export { computeView, computeViews, unsafeComputeView } from './compute-view'
2
+ export type { ComputeViewResult } from './compute-view'
3
3
  export { resolveRulesExtendedViews } from './utils/resolve-extended-views'
4
- export { withReadableEdges } from './utils/with-readable-edges'
5
- export { viewsWithReadableEdges } from './utils/with-readable-edges'
4
+ export { viewsWithReadableEdges, withReadableEdges } from './utils/with-readable-edges'
@@ -0,0 +1,45 @@
1
+ import type { AnyCtx, ComputeMemory, GenericCtx, MutableState, StageExpression } from './_types'
2
+
3
+ export abstract class AbstractMemory<T extends AnyCtx = GenericCtx> implements ComputeMemory<T> {
4
+ /**
5
+ * Provides access to context types
6
+ * !IMPORTANT: Should not be called in runtime
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * type State = SomeMemory['Ctx']['MutableState']
11
+ * ```
12
+ */
13
+ public get Ctx(): T {
14
+ throw new Error('Should not be called in runtime')
15
+ }
16
+
17
+ protected constructor(protected state: MutableState<T>) {
18
+ }
19
+
20
+ public get elements(): ReadonlySet<T['Element']> {
21
+ return this.state.elements
22
+ }
23
+ public get explicits(): ReadonlySet<T['Element']> {
24
+ return this.state.explicits
25
+ }
26
+ public get final(): ReadonlySet<T['Element']> {
27
+ return this.state.final
28
+ }
29
+ public get connections(): ReadonlyArray<T['Connection']> {
30
+ return this.state.connections
31
+ }
32
+
33
+ public isEmpty(): boolean {
34
+ return this.elements.size === 0 && this.connections.length === 0
35
+ && this.explicits.size === 0 && this.final.size === 0
36
+ }
37
+
38
+ abstract mutableState(): MutableState<T>
39
+
40
+ abstract stageInclude(expr: StageExpression<T>): T['StageInclude']
41
+
42
+ abstract stageExclude(expr: StageExpression<T>): T['StageExclude']
43
+
44
+ abstract update(newstate: Partial<MutableState<T>>): T['Memory']
45
+ }
@@ -0,0 +1,216 @@
1
+ import { isBoolean, pipe } from 'remeda'
2
+ import { invariant } from '../../errors'
3
+ import { findAscendingConnections } from '../../model/connection'
4
+ import { difference, intersection, isDescendantOf, isIterable } from '../../utils'
5
+ import { ifilter, isome, toSet } from '../../utils/iterable'
6
+ import type { AnyCtx, CtxConnection, CtxElement, MutableState, StageExclude, StageExpression } from './_types'
7
+
8
+ export abstract class AbstractStageExclude<T extends AnyCtx> implements StageExclude<T> {
9
+ // Removed elements
10
+ protected excluded = {
11
+ elements: new Set<CtxElement<T>>(),
12
+ connections: [] as CtxConnection<T>[],
13
+ }
14
+
15
+ constructor(
16
+ public readonly memory: T['Memory'],
17
+ public readonly expression: StageExpression<T>,
18
+ ) {
19
+ }
20
+
21
+ protected markedToMoveExplicitToImplicit = false
22
+
23
+ protected _removeElement(element: CtxElement<T>): void {
24
+ this.excluded.elements.add(element)
25
+ }
26
+
27
+ public exclude(element: CtxElement<T> | Iterable<CtxElement<T>> | false | undefined | null): this {
28
+ if (!element) {
29
+ return this
30
+ }
31
+ if (isIterable(element)) {
32
+ for (const el of element) {
33
+ this._removeElement(el)
34
+ }
35
+ return this
36
+ }
37
+ this._removeElement(element)
38
+ return this
39
+ }
40
+
41
+ protected _removeConnection(connection: CtxConnection<T>): void {
42
+ this.excluded.connections.push(connection)
43
+ }
44
+ /**
45
+ * Excludes from the memory relationships from given connections (still connection may be included, but without given relationships)
46
+ * @param moveExplicitToImplicit - if true, disconnected explicit elements will be moved to implicit
47
+ * @default false
48
+ */
49
+ public excludeConnections(
50
+ connection: CtxConnection<T> | Iterable<CtxConnection<T>>,
51
+ moveExplicitToImplicit?: boolean,
52
+ ) {
53
+ if (isBoolean(moveExplicitToImplicit)) {
54
+ invariant(!this.markedToMoveExplicitToImplicit, 'Already marked to move explicits')
55
+ this.markedToMoveExplicitToImplicit = moveExplicitToImplicit
56
+ }
57
+ if (isIterable(connection)) {
58
+ for (const c of connection) {
59
+ this._removeConnection(c)
60
+ }
61
+ return this
62
+ }
63
+ this._removeConnection(connection)
64
+ return this
65
+ }
66
+
67
+ public isDirty(): boolean {
68
+ return this.excluded.elements.size > 0 || this.excluded.connections.length > 0
69
+ }
70
+
71
+ public isEmpty(): boolean {
72
+ return !this.isDirty()
73
+ }
74
+
75
+ /**
76
+ * Determines whether disconnected explicits should become implicits.
77
+ * By default moves all disconnected explicits to implicits, if there were operation to exclude elements.
78
+ *
79
+ * Override this method to change the behavior.
80
+ */
81
+ protected filterForMoveToImplicits(disconnectedExplicits: Set<CtxElement<T>>): Set<CtxElement<T>> {
82
+ if (this.markedToMoveExplicitToImplicit || this.excluded.elements.size > 0) {
83
+ return disconnectedExplicits
84
+ }
85
+ return new Set()
86
+ }
87
+
88
+ // Check if Leaf EXPLICIT elements are becoming IMPLICIT
89
+ // (it means that they are not connected anymore)
90
+ protected moveDisconnectedExplicitsToImplicits(state: MutableState<T>): MutableState<T> {
91
+ // Check if EXPLICIT elements are becoming IMPLICIT
92
+ let disconnected = difference(
93
+ new Set(this.memory.connections.flatMap(c => [c.source, c.target])),
94
+ new Set(state.connections.flatMap(c => [c.source, c.target])),
95
+ )
96
+ // Ensure existing in implicits
97
+ disconnected = intersection(
98
+ disconnected,
99
+ state.elements,
100
+ )
101
+ // Ensure this is a leaf explici element
102
+ disconnected = pipe(
103
+ disconnected,
104
+ ifilter(el => {
105
+ return state.explicits.has(el) && !isome(state.final, isDescendantOf(el))
106
+ }),
107
+ toSet(),
108
+ )
109
+ if (disconnected.size > 0) {
110
+ disconnected = this.filterForMoveToImplicits(disconnected)
111
+ state.explicits = difference(state.explicits, disconnected)
112
+ // Exclude from final elements
113
+ state.final = difference(state.final, disconnected)
114
+ }
115
+ return state
116
+ }
117
+
118
+ protected removeElements(state: MutableState<T>): MutableState<T> {
119
+ state.elements = difference(state.elements, this.excluded.elements)
120
+ state.explicits = difference(state.explicits, this.excluded.elements)
121
+ state.final = difference(state.final, this.excluded.elements)
122
+ return state
123
+ }
124
+
125
+ protected removeConnections(state: MutableState<T>): MutableState<T> {
126
+ const excludedMap = this.excluded.connections.reduce((acc, c) => {
127
+ const existing = acc.get(c.id)
128
+ if (existing) {
129
+ acc.set(c.id, existing.mergeWith(c) as CtxConnection<T>)
130
+ } else {
131
+ acc.set(c.id, c)
132
+ }
133
+ return acc
134
+ }, new Map<string, CtxConnection<T>>())
135
+
136
+ // were connected before and not anymore
137
+ let disconnected = new Set<CtxElement<T>>()
138
+
139
+ state.connections = state.connections.reduce((acc, c) => {
140
+ const excluded = excludedMap.get(c.id)
141
+ if (excluded) {
142
+ disconnected.add(c.source)
143
+ disconnected.add(c.target)
144
+ const diff = c.difference(excluded) as CtxConnection<T>
145
+ if (diff.nonEmpty()) {
146
+ acc.push(diff)
147
+ }
148
+ } else {
149
+ acc.push(c)
150
+ }
151
+ return acc
152
+ }, [] as CtxConnection<T>[])
153
+
154
+ for (const stillExists of state.connections) {
155
+ disconnected.delete(stillExists.source)
156
+ disconnected.delete(stillExists.target)
157
+ if (stillExists.boundary && state.elements.has(stillExists.boundary)) {
158
+ disconnected.delete(stillExists.boundary)
159
+ }
160
+ }
161
+
162
+ if (disconnected.size === 0) {
163
+ return state
164
+ }
165
+ // Keep explicits
166
+ disconnected = difference(disconnected, state.explicits)
167
+ // Even if not connected, implicit elements should be kept
168
+ // state.elements = difference(state.elements, excludedElements)
169
+ state.final = difference(state.final, disconnected)
170
+
171
+ return state
172
+ }
173
+
174
+ /**
175
+ * Precommit hook
176
+ */
177
+ protected precommit(state: MutableState<T>): MutableState<T> {
178
+ return state
179
+ }
180
+
181
+ /**
182
+ * Postcommit hook
183
+ */
184
+ protected postcommit(state: MutableState<T>): MutableState<T> {
185
+ invariant(difference(state.explicits, state.elements).size === 0, 'Explicits must be subset of elements')
186
+ invariant(difference(state.final, state.elements).size === 0, 'Final elements must be subset of elements')
187
+ return state
188
+ }
189
+
190
+ public commit(): T['Memory'] {
191
+ let state = this.precommit(this.memory.mutableState())
192
+
193
+ // Exclude connections from excluded elements
194
+ if (this.excluded.elements.size > 0) {
195
+ const excludedConnections = state.connections
196
+ .filter(c => this.excluded.elements.has(c.source) || this.excluded.elements.has(c.target))
197
+ .flatMap(c => [
198
+ c,
199
+ ...findAscendingConnections(state.connections, c)
200
+ .map(asc => asc.intersect(c) as CtxConnection<T>),
201
+ ])
202
+ .filter(asc => asc.nonEmpty())
203
+ this.excludeConnections(excludedConnections)
204
+ }
205
+ if (this.excluded.connections.length > 0) {
206
+ state = this.removeConnections(state)
207
+ }
208
+ if (this.excluded.elements.size > 0) {
209
+ state = this.removeElements(state)
210
+ }
211
+
212
+ state = this.moveDisconnectedExplicitsToImplicits(state)
213
+
214
+ return this.memory.update(this.postcommit(state))
215
+ }
216
+ }
@@ -0,0 +1,200 @@
1
+ import { partition } from 'remeda'
2
+ import { mergeConnections } from '../../model/connection'
3
+ import { intersection, isIterable, union } from '../../utils'
4
+ import type {
5
+ AnyCtx,
6
+ CtxConnection as Connection,
7
+ CtxElement as Elem,
8
+ GenericCtx,
9
+ MutableState,
10
+ StageExpression,
11
+ StageInclude,
12
+ } from './_types'
13
+
14
+ export abstract class AbstractStageInclude<T extends AnyCtx = GenericCtx> implements StageInclude<T> {
15
+ // New elements
16
+ protected explicits = new Set<Elem<T>>()
17
+ protected implicits = new Set<Elem<T>>()
18
+
19
+ // Ordered Set of explicit and implicit elements
20
+ protected _ordered = new Set<Elem<T>>()
21
+
22
+ protected _connections = [] as Connection<T>[]
23
+
24
+ constructor(
25
+ public readonly memory: T['Memory'],
26
+ public readonly expression: StageExpression<T>,
27
+ ) {
28
+ }
29
+
30
+ get elements(): ReadonlySet<Elem<T>> {
31
+ return this.explicits
32
+ }
33
+
34
+ /**
35
+ * Connections from this stage
36
+ */
37
+ get connections(): readonly Connection<T>[] {
38
+ return this._connections
39
+ }
40
+
41
+ mergedConnections(): readonly Connection<T>[] {
42
+ return mergeConnections([
43
+ ...this.memory.connections,
44
+ ...this._connections,
45
+ ])
46
+ }
47
+
48
+ public connectWithExisting(
49
+ _element: Elem<T> | Iterable<Elem<T>>,
50
+ _direction?: 'in' | 'out' | 'both',
51
+ ): boolean {
52
+ throw new Error('Method not implements, depends on the model')
53
+ }
54
+
55
+ /**
56
+ * Possible to override
57
+ */
58
+ protected _addExplicit(elements: Elem<T>): void {
59
+ this._ordered.add(elements)
60
+ this.explicits.add(elements)
61
+ this.implicits.delete(elements)
62
+ }
63
+
64
+ public addExplicit(element: Elem<T> | Iterable<Elem<T>> | false | undefined | null): this {
65
+ if (!element) {
66
+ return this
67
+ }
68
+ if (isIterable(element)) {
69
+ for (const el of element) {
70
+ this._addExplicit(el)
71
+ }
72
+ return this
73
+ }
74
+ this._addExplicit(element)
75
+ return this
76
+ }
77
+
78
+ /**
79
+ * Possible to override
80
+ */
81
+ protected _addImplicit(elements: Elem<T>): void {
82
+ if (this.explicits.has(elements)) {
83
+ return
84
+ }
85
+ this._ordered.add(elements)
86
+ this.implicits.add(elements)
87
+ }
88
+
89
+ public addImplicit(elements: Elem<T> | Iterable<Elem<T>> | false | undefined | null): this {
90
+ if (!elements) {
91
+ return this
92
+ }
93
+ if (isIterable(elements)) {
94
+ for (const el of elements) {
95
+ this._addImplicit(el)
96
+ }
97
+ return this
98
+ }
99
+ this._addImplicit(elements)
100
+ return this
101
+ }
102
+
103
+ /**
104
+ * Possible to override
105
+ */
106
+ protected _addConnection(connection: Connection<T>): void {
107
+ this._connections.push(connection)
108
+ this._addImplicit(connection.source)
109
+ this._addImplicit(connection.target)
110
+ }
111
+
112
+ public addConnections(connection: Connection<T> | Iterable<Connection<T>>): this {
113
+ if (isIterable(connection)) {
114
+ for (const c of connection) {
115
+ this._addConnection(c)
116
+ }
117
+ return this
118
+ }
119
+ this._addConnection(connection)
120
+ return this
121
+ }
122
+
123
+ public isDirty(): boolean {
124
+ return this.explicits.size > 0 || this.implicits.size > 0 || this._connections.length > 0
125
+ }
126
+
127
+ public isEmpty(): boolean {
128
+ return !this.isDirty()
129
+ }
130
+
131
+ /**
132
+ * Precommit hook
133
+ */
134
+ protected precommit(state: MutableState<T>): MutableState<T> {
135
+ return state
136
+ }
137
+
138
+ /**
139
+ * Postcommit hook
140
+ */
141
+ protected postcommit(state: MutableState<T>): MutableState<T> {
142
+ return state
143
+ }
144
+
145
+ protected processConnections(connections: Connection<T>[]): Connection<T>[] { // To preserve order, we split new connections into two sets
146
+ return connections
147
+ }
148
+
149
+ public commit(): T['Memory'] {
150
+ let state = this.precommit(this.memory.mutableState())
151
+
152
+ let fromConnections = new Set<Elem<T>>()
153
+
154
+ if (this._connections.length > 0) {
155
+ // To preserve order, we split new connections into two sets
156
+ // First are outgoing from known elements (in memory.elements)
157
+ const [fromKnown, rest] = partition(
158
+ this._connections,
159
+ c => state.final.has(c.source),
160
+ )
161
+
162
+ state.connections = this.processConnections(
163
+ mergeConnections([
164
+ ...state.connections,
165
+ ...fromKnown,
166
+ ...rest,
167
+ ]),
168
+ )
169
+
170
+ fromConnections = new Set(state.connections.flatMap(c => [c.source, c.target]))
171
+ }
172
+
173
+ state.elements = union(
174
+ state.elements,
175
+ this._ordered,
176
+ this.explicits,
177
+ fromConnections,
178
+ this.implicits,
179
+ )
180
+
181
+ state.explicits = intersection(
182
+ state.elements,
183
+ union(
184
+ state.explicits,
185
+ this.explicits,
186
+ ),
187
+ )
188
+
189
+ state.final = intersection(
190
+ state.elements,
191
+ union(
192
+ state.final,
193
+ this.explicits,
194
+ fromConnections,
195
+ ),
196
+ )
197
+
198
+ return this.memory.update(this.postcommit(state))
199
+ }
200
+ }