@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.
- package/LICENSE +1 -1
- package/dist/builder/index.d.mts +502 -0
- package/dist/builder/index.d.ts +502 -0
- package/dist/builder/index.js +871 -0
- package/dist/compute-view/index.d.mts +9 -78
- package/dist/compute-view/index.d.ts +9 -78
- package/dist/compute-view/index.js +78 -0
- package/dist/index.d.mts +9 -730
- package/dist/index.d.ts +9 -730
- package/dist/{index.mjs → index.js} +7 -784
- package/dist/model/index.d.mts +218 -606
- package/dist/model/index.d.ts +218 -606
- package/dist/model/index.js +1 -0
- package/dist/shared/chunk-RAAYCPUM.M-JWF7SS.js +21 -0
- package/dist/shared/core.BIfgabEw.d.ts +719 -0
- package/dist/shared/core.BZ9Msq7w.d.mts +719 -0
- package/dist/shared/{core.DMYY7C8s.d.mts → core.C05RDLhi.d.ts} +179 -230
- package/dist/shared/{core.DMYY7C8s.d.ts → core.CmYMqgha.d.mts} +179 -230
- package/dist/shared/core.D6-fWbM3.d.ts +127 -0
- package/dist/shared/core.D74xkKkG.d.mts +148 -0
- package/dist/shared/core.D74xkKkG.d.ts +148 -0
- package/dist/shared/core.sLaWHBjR.d.mts +127 -0
- package/dist/shared/index.-BdzdI2C.js +7443 -0
- package/dist/shared/object_hash.CE_oF3I3.js +1222 -0
- package/dist/shared/view.CyZrG8BJ.js +354 -0
- package/dist/types/index.d.mts +4 -2
- package/dist/types/index.d.ts +4 -2
- package/dist/types/index.js +20 -0
- package/dist/utils/index.d.mts +404 -0
- package/dist/utils/index.d.ts +404 -0
- package/dist/utils/index.js +330 -0
- package/package.json +47 -33
- package/src/builder/Builder-style1.test-d.ts +190 -0
- package/src/builder/Builder-style2.test-d.ts +175 -0
- package/src/builder/Builder.deployment.ts +25 -20
- package/src/builder/Builder.deploymentModel.ts +80 -55
- package/src/builder/Builder.element.ts +2 -0
- package/src/builder/Builder.model.ts +57 -43
- package/src/builder/Builder.ts +412 -431
- package/src/builder/Builder.view-common.ts +234 -0
- package/src/builder/Builder.view-deployment.ts +135 -0
- package/src/builder/Builder.view-element.ts +189 -0
- package/src/builder/Builder.views.ts +109 -33
- package/src/builder/Builder.with.ts +475 -0
- package/src/builder/__snapshots__/Builder-style1.spec.ts.snap +374 -0
- package/src/builder/__snapshots__/Builder-style2.spec.ts.snap +183 -0
- package/src/builder/__snapshots__/Builder.spec.ts.snap +374 -0
- package/src/builder/_types.ts +28 -5
- package/src/builder/index.ts +10 -4
- package/src/colors/index.ts +2 -2
- package/src/compute-view/compute-view.ts +45 -54
- package/src/compute-view/deployment-view/__test__/TestHelper.ts +236 -0
- package/src/compute-view/deployment-view/__test__/fixture.ts +50 -62
- package/src/compute-view/deployment-view/_types.ts +26 -18
- package/src/compute-view/deployment-view/clean-connections.ts +173 -164
- package/src/compute-view/deployment-view/compute.ts +51 -37
- package/src/compute-view/deployment-view/memory/index.ts +9 -0
- package/src/compute-view/deployment-view/memory/memory.ts +97 -0
- package/src/compute-view/deployment-view/predicates/elements.ts +144 -0
- package/src/compute-view/deployment-view/predicates/relation-direct.ts +183 -76
- package/src/compute-view/deployment-view/predicates/relation-in-out.ts +38 -20
- package/src/compute-view/deployment-view/predicates/relation-incoming.ts +72 -23
- package/src/compute-view/deployment-view/predicates/relation-outgoing.ts +61 -22
- package/src/compute-view/deployment-view/predicates/wildcard.ts +15 -15
- package/src/compute-view/deployment-view/stages/stage-exclude.ts +5 -0
- package/src/compute-view/deployment-view/stages/stage-final.ts +245 -0
- package/src/compute-view/deployment-view/stages/stage-include.ts +95 -0
- package/src/compute-view/deployment-view/utils.ts +95 -31
- package/src/compute-view/dynamic-view/__test__/fixture.ts +4 -4
- package/src/compute-view/dynamic-view/compute.ts +59 -45
- package/src/compute-view/element-view/__test__/TestHelper.ts +258 -0
- package/src/compute-view/element-view/__test__/__snapshots__/legacy.spec.ts.snap +64 -62
- package/src/compute-view/element-view/__test__/fixture.ts +147 -117
- package/src/compute-view/element-view/_types.ts +48 -0
- package/src/compute-view/element-view/clean-connections.ts +113 -0
- package/src/compute-view/element-view/compute.ts +264 -683
- package/src/compute-view/element-view/memory/NodeGroup.ts +31 -0
- package/src/compute-view/element-view/memory/index.ts +10 -0
- package/src/compute-view/element-view/memory/memory.ts +177 -0
- package/src/compute-view/element-view/memory/stage-exclude.ts +59 -0
- package/src/compute-view/element-view/memory/stage-final.ts +129 -0
- package/src/compute-view/element-view/memory/stage-include.ts +128 -0
- package/src/compute-view/element-view/predicates/_utils.ts +86 -0
- package/src/compute-view/element-view/predicates/element-expand.ts +33 -0
- package/src/compute-view/element-view/predicates/element-kind-tag.ts +26 -0
- package/src/compute-view/element-view/predicates/element-ref.ts +37 -0
- package/src/compute-view/element-view/predicates/relation-direct.ts +309 -0
- package/src/compute-view/element-view/predicates/relation-in-out.ts +66 -0
- package/src/compute-view/element-view/predicates/relation-in.ts +118 -0
- package/src/compute-view/element-view/predicates/relation-out.ts +116 -0
- package/src/compute-view/element-view/predicates/wildcard.ts +88 -0
- package/src/compute-view/element-view/utils.ts +59 -0
- package/src/compute-view/index.ts +3 -4
- package/src/compute-view/memory/AbstractMemory.ts +45 -0
- package/src/compute-view/memory/AbstractStageExclude.ts +216 -0
- package/src/compute-view/memory/AbstractStageInclude.ts +200 -0
- package/src/compute-view/memory/_types.ts +147 -0
- package/src/compute-view/memory/index.ts +5 -0
- package/src/compute-view/memory/ops.ts +43 -0
- package/src/compute-view/utils/buildComputedNodes.ts +4 -4
- package/src/compute-view/utils/elementExpressionToPredicate.ts +1 -3
- package/src/compute-view/utils/link-nodes-with-edges.ts +3 -7
- package/src/compute-view/utils/{derive-edge-props-from-relationships.ts → merge-props-from-relationships.ts} +54 -23
- package/src/compute-view/utils/relationExpressionToPredicates.ts +2 -2
- package/src/compute-view/utils/resolve-extended-views.ts +44 -40
- package/src/compute-view/utils/resolve-global-rules.ts +6 -6
- package/src/compute-view/utils/topological-sort.ts +167 -71
- package/src/compute-view/utils/with-readable-edges.ts +14 -9
- package/src/index.ts +22 -16
- package/src/model/DeploymentElementModel.ts +150 -85
- package/src/model/DeploymentModel.ts +27 -21
- package/src/model/ElementModel.ts +51 -25
- package/src/model/LikeC4Model.test-d.ts +4 -3
- package/src/model/LikeC4Model.ts +98 -42
- package/src/model/RelationModel.ts +5 -4
- package/src/model/__test__/fixture.ts +5 -9
- package/src/model/connection/Connection.ts +72 -0
- package/src/model/connection/deployment/DeploymentConnectionModel.ts +178 -0
- package/src/model/connection/{deployment.ts → deployment/find.ts} +14 -18
- package/src/model/connection/deployment/index.ts +8 -0
- package/src/model/connection/index.ts +22 -0
- package/src/model/connection/model/ConnectionModel.ts +157 -0
- package/src/model/connection/{model.ts → model/find.ts} +43 -49
- package/src/model/connection/model/index.ts +8 -0
- package/src/model/connection/ops.ts +274 -0
- package/src/model/guards.ts +9 -0
- package/src/model/index.ts +45 -3
- package/src/model/types.ts +35 -18
- package/src/model/view/EdgeModel.ts +6 -6
- package/src/model/view/LikeC4ViewModel.ts +14 -8
- package/src/model/view/NodeModel.ts +6 -5
- package/src/types/_common.ts +53 -1
- package/src/types/deployments.ts +88 -156
- package/src/types/element.ts +2 -2
- package/src/types/expression-v2.ts +172 -0
- package/src/types/expression.ts +9 -5
- package/src/types/global.ts +3 -3
- package/src/types/index.ts +220 -13
- package/src/types/model.ts +28 -24
- package/src/types/operators.ts +1 -1
- package/src/types/relation.ts +14 -10
- package/src/types/theme.ts +0 -1
- package/src/types/view.ts +20 -18
- package/src/utils/const.ts +1 -0
- package/src/utils/fqn.ts +140 -49
- package/src/utils/getOrCreate.ts +1 -1
- package/src/utils/index.ts +74 -9
- package/src/utils/iterable/_types.ts +5 -0
- package/src/utils/iterable/filter.ts +55 -0
- package/src/utils/iterable/flat.ts +17 -0
- package/src/utils/iterable/index.ts +12 -0
- package/src/utils/iterable/map.ts +40 -0
- package/src/utils/iterable/reduce.ts +24 -0
- package/src/utils/iterable/some.ts +36 -0
- package/src/utils/iterable/to.ts +19 -0
- package/src/utils/iterable/unique.ts +29 -0
- package/src/utils/relations.ts +3 -64
- package/src/utils/set.ts +11 -9
- package/src/utils/string-hash.ts +1 -1
- package/dist/compute-view/index.mjs +0 -79
- package/dist/model/index.mjs +0 -2
- package/dist/shared/core.BBgiAAv1.mjs +0 -144
- package/dist/shared/core.BPyJvuMU.mjs +0 -2420
- package/dist/shared/core.BQ4AbfvB.mjs +0 -9228
- package/dist/shared/core.k12s_keD.mjs +0 -203
- package/dist/types/index.mjs +0 -21
- package/src/builder/Builder.test-d.ts +0 -124
- package/src/builder/Builder.view.ts +0 -543
- package/src/compute-view/LikeC4ModelGraph.ts +0 -330
- package/src/compute-view/deployment-view/Memory.ts +0 -108
- package/src/compute-view/deployment-view/Stage.ts +0 -145
- package/src/compute-view/deployment-view/predicates/deployment-ref.ts +0 -146
- package/src/compute-view/element-view/predicates.ts +0 -517
- package/src/compute-view/utils/sortNodes.ts +0 -101
- package/src/model/connection/ConnectionModel.ts +0 -94
- package/src/model/connection/DeploymentConnectionModel.ts +0 -99
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ElementKind, type NodeId, type ViewRuleGroup } from '../../../types'
|
|
2
|
+
import type { Elem } from '../_types'
|
|
3
|
+
|
|
4
|
+
export class NodesGroup {
|
|
5
|
+
static readonly kind = ElementKind.Group
|
|
6
|
+
|
|
7
|
+
constructor(
|
|
8
|
+
public readonly id: NodeId,
|
|
9
|
+
public readonly viewRule: ViewRuleGroup,
|
|
10
|
+
public readonly parent: NodeId | null = null,
|
|
11
|
+
public readonly elements: ReadonlySet<Elem> = new Set<Elem>()
|
|
12
|
+
) {
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
isEmpty() {
|
|
16
|
+
return this.elements.size === 0
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
update(elements: ReadonlySet<Elem>): NodesGroup {
|
|
20
|
+
return new NodesGroup(
|
|
21
|
+
this.id,
|
|
22
|
+
this.viewRule,
|
|
23
|
+
this.parent,
|
|
24
|
+
elements
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
clone() {
|
|
29
|
+
return new NodesGroup(this.id, this.viewRule, this.parent, new Set(this.elements))
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { StageExclude } from './stage-exclude'
|
|
2
|
+
import type { StageInclude } from './stage-include'
|
|
3
|
+
|
|
4
|
+
export type { Ctx } from './memory'
|
|
5
|
+
export { Memory } from './memory'
|
|
6
|
+
export { NodesGroup } from './NodeGroup'
|
|
7
|
+
export { StageExclude } from './stage-exclude'
|
|
8
|
+
export { StageInclude } from './stage-include'
|
|
9
|
+
|
|
10
|
+
export type Stage = StageInclude | StageExclude
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import Stack from 'mnemonist/stack'
|
|
2
|
+
import { nonNullable } from '../../../errors'
|
|
3
|
+
import type { ConnectionModel } from '../../../model/connection'
|
|
4
|
+
import type { ElementModel } from '../../../model/ElementModel'
|
|
5
|
+
import type { Expression, NodeId, ViewRuleGroup } from '../../../types'
|
|
6
|
+
import { AbstractMemory, type ComputeCtx, type CtxElement, type MutableState, type StageExpression } from '../../memory'
|
|
7
|
+
import { NodesGroup } from './NodeGroup'
|
|
8
|
+
import { ActiveGroupStageExclude, StageExclude } from './stage-exclude'
|
|
9
|
+
import { ActiveGroupStageInclude, StageInclude } from './stage-include'
|
|
10
|
+
|
|
11
|
+
export interface Ctx extends
|
|
12
|
+
ComputeCtx<
|
|
13
|
+
ElementModel,
|
|
14
|
+
ConnectionModel,
|
|
15
|
+
Memory<Ctx>,
|
|
16
|
+
StageInclude<Ctx>,
|
|
17
|
+
StageExclude,
|
|
18
|
+
Expression
|
|
19
|
+
>
|
|
20
|
+
{
|
|
21
|
+
MutableState: {
|
|
22
|
+
elements: Set<ElementModel>
|
|
23
|
+
explicits: Set<ElementModel>
|
|
24
|
+
final: Set<ElementModel>
|
|
25
|
+
connections: ConnectionModel[]
|
|
26
|
+
groups: NodesGroup[]
|
|
27
|
+
/*
|
|
28
|
+
* The group where explict element was added first time
|
|
29
|
+
* May be a root group
|
|
30
|
+
*/
|
|
31
|
+
explicitFirstSeenIn: Map<ElementModel, NodesGroup['id']>
|
|
32
|
+
/**
|
|
33
|
+
* The group where element (implicit or explicit) was last seen (added or updated)
|
|
34
|
+
* Exclude root group
|
|
35
|
+
*/
|
|
36
|
+
lastSeenIn: Map<ElementModel, NodesGroup['id']>
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class Memory<C extends Ctx = Ctx> extends AbstractMemory<C> {
|
|
41
|
+
static empty(scope: CtxElement<Ctx> | null): Memory {
|
|
42
|
+
return new Memory({
|
|
43
|
+
elements: new Set(),
|
|
44
|
+
explicits: new Set(),
|
|
45
|
+
final: new Set(),
|
|
46
|
+
connections: [],
|
|
47
|
+
groups: [],
|
|
48
|
+
explicitFirstSeenIn: new Map(),
|
|
49
|
+
lastSeenIn: new Map(),
|
|
50
|
+
}, scope)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
protected constructor(
|
|
54
|
+
protected override state: MutableState<C>,
|
|
55
|
+
public readonly scope: CtxElement<C> | null,
|
|
56
|
+
) {
|
|
57
|
+
super(state)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public get groups(): ReadonlyArray<NodesGroup> {
|
|
61
|
+
return this.state.groups
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public get explicitFirstSeenIn(): ReadonlyMap<ElementModel, NodesGroup['id']> {
|
|
65
|
+
return this.state.explicitFirstSeenIn
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public get lastSeenIn(): ReadonlyMap<ElementModel, NodesGroup['id']> {
|
|
69
|
+
return this.state.lastSeenIn
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
override stageInclude(expr: StageExpression<Ctx>): StageInclude {
|
|
73
|
+
return new StageInclude(this, expr)
|
|
74
|
+
}
|
|
75
|
+
override stageExclude(expr: StageExpression<Ctx>): StageExclude {
|
|
76
|
+
return new StageExclude(this, expr)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
override mutableState(): MutableState<C> {
|
|
80
|
+
return ({
|
|
81
|
+
elements: new Set(this.state.elements),
|
|
82
|
+
explicits: new Set(this.state.explicits),
|
|
83
|
+
final: new Set(this.state.final),
|
|
84
|
+
connections: [...this.state.connections],
|
|
85
|
+
groups: this.state.groups.map((g) => g.clone()),
|
|
86
|
+
explicitFirstSeenIn: new Map(this.state.explicitFirstSeenIn),
|
|
87
|
+
lastSeenIn: new Map(this.state.lastSeenIn),
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
override update(newstate: Partial<C['MutableState']>): Memory {
|
|
92
|
+
return new Memory({
|
|
93
|
+
...this.state,
|
|
94
|
+
...newstate,
|
|
95
|
+
}, this.scope)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
type ActiveGroupState = Ctx['MutableState'] & {
|
|
100
|
+
// activeGroup: NodesGroup
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface ActiveGroupCtx extends Ctx {
|
|
104
|
+
MutableState: ActiveGroupState
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export class ActiveGroupMemory extends Memory<ActiveGroupCtx> {
|
|
108
|
+
static enter(
|
|
109
|
+
memory: Memory,
|
|
110
|
+
rule: ViewRuleGroup,
|
|
111
|
+
): ActiveGroupMemory {
|
|
112
|
+
const groupId = `@gr${memory.groups.length + 1}` as NodeId
|
|
113
|
+
if (memory instanceof ActiveGroupMemory) {
|
|
114
|
+
const stack = Stack.from(memory.stack)
|
|
115
|
+
const state = memory.mutableState()
|
|
116
|
+
state.groups.push(new NodesGroup(groupId, rule, memory.activeGroupId))
|
|
117
|
+
stack.push(groupId)
|
|
118
|
+
return new ActiveGroupMemory(state, memory.scope, stack)
|
|
119
|
+
}
|
|
120
|
+
const state = memory.mutableState() as ActiveGroupState
|
|
121
|
+
state.groups.push(new NodesGroup(groupId, rule, null))
|
|
122
|
+
const stack = Stack.of(groupId)
|
|
123
|
+
return new ActiveGroupMemory(state, memory.scope, stack)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
protected constructor(
|
|
127
|
+
protected override state: ActiveGroupState,
|
|
128
|
+
public override readonly scope: CtxElement<ActiveGroupCtx> | null,
|
|
129
|
+
// Stack of group ids
|
|
130
|
+
protected stack: Stack<NodeId>,
|
|
131
|
+
) {
|
|
132
|
+
super(state, scope)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
get activeGroupId(): NodeId {
|
|
136
|
+
return nonNullable(this.stack.peek(), 'Stack must not be empty')
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
override mutableState(): ActiveGroupState {
|
|
140
|
+
const state = super.mutableState()
|
|
141
|
+
return ({
|
|
142
|
+
...state,
|
|
143
|
+
// activeGroup: this.findActiveGroup(state.groups)
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// private findActiveGroup(groups: NodesGroup[]): NodesGroup {
|
|
148
|
+
// return nonNullable(groups.find(g => g.id === this.activeGroup.id), 'Active group not found in groups')
|
|
149
|
+
// }
|
|
150
|
+
|
|
151
|
+
override update(newstate: Partial<ActiveGroupState>): ActiveGroupMemory {
|
|
152
|
+
const nextstate = {
|
|
153
|
+
...this.state,
|
|
154
|
+
...newstate,
|
|
155
|
+
}
|
|
156
|
+
// const activeGroup = this.findActiveGroup(nextstate.groups)
|
|
157
|
+
// invariant(Object.is(activeGroup, nextstate.activeGroup), 'Active group must strictly equal to the group in groups')
|
|
158
|
+
return new ActiveGroupMemory(nextstate, this.scope, this.stack)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
override stageInclude(expr: StageExpression<Ctx>): ActiveGroupStageInclude {
|
|
162
|
+
return new ActiveGroupStageInclude(this, expr)
|
|
163
|
+
}
|
|
164
|
+
override stageExclude(expr: StageExpression<Ctx>): ActiveGroupStageExclude {
|
|
165
|
+
return new ActiveGroupStageExclude(this, expr)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
public leave(): Memory {
|
|
169
|
+
const state = this.mutableState()
|
|
170
|
+
this.stack.pop()
|
|
171
|
+
const prevgroup = this.stack.peek()
|
|
172
|
+
if (prevgroup) {
|
|
173
|
+
return new ActiveGroupMemory(state, this.scope, this.stack)
|
|
174
|
+
}
|
|
175
|
+
return new Memory(state, this.scope)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { filter, forEach, pipe } from 'remeda'
|
|
2
|
+
import type { RelationshipModel } from '../../../model/RelationModel'
|
|
3
|
+
import { difference, hasIntersection, intersection } from '../../../utils/set'
|
|
4
|
+
import { AbstractStageExclude, type MutableState, type StageExpression } from '../../memory'
|
|
5
|
+
import { type ActiveGroupCtx, type ActiveGroupMemory, type Ctx } from './memory'
|
|
6
|
+
|
|
7
|
+
export class StageExclude<C extends Ctx = Ctx> extends AbstractStageExclude<C> {
|
|
8
|
+
public excludeRelations(excluded: ReadonlySet<RelationshipModel<any>>): this {
|
|
9
|
+
pipe(
|
|
10
|
+
this.memory.connections,
|
|
11
|
+
filter(c => hasIntersection(c.relations, excluded)),
|
|
12
|
+
forEach(c => {
|
|
13
|
+
this.excludeConnections(
|
|
14
|
+
c.update(intersection(c.relations, excluded)),
|
|
15
|
+
)
|
|
16
|
+
}),
|
|
17
|
+
)
|
|
18
|
+
return this
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Precommit hook
|
|
23
|
+
*/
|
|
24
|
+
protected override precommit(state: MutableState<C>): MutableState<C> {
|
|
25
|
+
if (this.excluded.elements.size > 0) {
|
|
26
|
+
const excludeRelationships = new Set(
|
|
27
|
+
[...this.excluded.elements].flatMap(el => [
|
|
28
|
+
...el.incoming('direct'),
|
|
29
|
+
...el.outgoing('direct'),
|
|
30
|
+
]),
|
|
31
|
+
)
|
|
32
|
+
this.excludeRelations(excludeRelationships)
|
|
33
|
+
}
|
|
34
|
+
return state
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
protected override postcommit(state: C['MutableState']) {
|
|
38
|
+
const leftExplicits = difference(this.memory.explicits, state.explicits)
|
|
39
|
+
for (const explicit of leftExplicits) {
|
|
40
|
+
state.explicitFirstSeenIn.delete(explicit)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Left elements
|
|
44
|
+
const left = difference(this.memory.elements, state.elements)
|
|
45
|
+
for (const el of left) {
|
|
46
|
+
state.lastSeenIn.delete(el)
|
|
47
|
+
}
|
|
48
|
+
return state
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class ActiveGroupStageExclude extends StageExclude {
|
|
53
|
+
constructor(
|
|
54
|
+
public override readonly memory: ActiveGroupMemory,
|
|
55
|
+
public override readonly expression: StageExpression<ActiveGroupCtx>,
|
|
56
|
+
) {
|
|
57
|
+
super(memory, expression)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { forEach, only, pipe } from 'remeda'
|
|
2
|
+
import { isAncestor, sortByFqnHierarchically } from '../../../utils/fqn'
|
|
3
|
+
import { isome, toArray } from '../../../utils/iterable'
|
|
4
|
+
import { difference, union } from '../../../utils/set'
|
|
5
|
+
import { treeFromMemoryState } from '../../memory/ops'
|
|
6
|
+
import type { Elem } from '../_types'
|
|
7
|
+
import { findRedundantConnections } from '../clean-connections'
|
|
8
|
+
import type { Ctx, Memory } from './memory'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* This patch:
|
|
12
|
+
* 1. Keeps connections between leafs or having direct deployment relations
|
|
13
|
+
* 2. Removes cross-boundary model relations, that already exist inside boundaries
|
|
14
|
+
* (e.g. prefer relations inside same deployment node over relations between nodes)
|
|
15
|
+
* 3. Removes implicit connections between elements, if their descendants have same connection
|
|
16
|
+
*/
|
|
17
|
+
export class StageFinal {
|
|
18
|
+
static for(memory: Memory) {
|
|
19
|
+
return new StageFinal(memory)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private constructor(
|
|
23
|
+
protected readonly memory: Memory,
|
|
24
|
+
) {
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public step1CleanConnections(memory: Memory): Memory {
|
|
28
|
+
if (memory.connections.length < 2) {
|
|
29
|
+
return memory
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const connectionsToExclude = findRedundantConnections(memory.connections)
|
|
33
|
+
if (connectionsToExclude.length === 0) {
|
|
34
|
+
return memory
|
|
35
|
+
}
|
|
36
|
+
const stage = memory.stageExclude({} as any)
|
|
37
|
+
|
|
38
|
+
stage.excludeConnections(connectionsToExclude, true)
|
|
39
|
+
|
|
40
|
+
return stage.commit()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public step2ProcessImplicits(memory: Memory): Memory {
|
|
44
|
+
const implicits = union(this.memory.elements, memory.elements)
|
|
45
|
+
const final = union(memory.final, this.memory.explicits)
|
|
46
|
+
|
|
47
|
+
const snapshot = new Set(final)
|
|
48
|
+
const isFinalOrHasIncludedDescendant = (el: Elem) => {
|
|
49
|
+
for (const final of snapshot) {
|
|
50
|
+
if (el === final || isAncestor(el, final)) {
|
|
51
|
+
return true
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return false
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Pick from implicit elements
|
|
58
|
+
pipe(
|
|
59
|
+
difference(implicits, snapshot),
|
|
60
|
+
toArray(),
|
|
61
|
+
sortByFqnHierarchically,
|
|
62
|
+
forEach((el) => {
|
|
63
|
+
// if (memory.scope && !memory.scope.isAncestorOf(el)) {
|
|
64
|
+
// return
|
|
65
|
+
// }
|
|
66
|
+
// If element has more 2 or more children included
|
|
67
|
+
// It can "box" around
|
|
68
|
+
const childrensToWrap = [...el.children()].filter(isFinalOrHasIncludedDescendant).length
|
|
69
|
+
if (childrensToWrap >= 2) {
|
|
70
|
+
final.add(el)
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
// If Element has only one child included
|
|
74
|
+
// and has "similar" sibling with included descendant
|
|
75
|
+
if (childrensToWrap === 1 && isome(el.siblings(), isFinalOrHasIncludedDescendant)) {
|
|
76
|
+
final.add(el)
|
|
77
|
+
}
|
|
78
|
+
}),
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return memory.update({ final })
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public step3ProcessBoundaries(memory: Memory): Memory {
|
|
85
|
+
const boundaries = new Set<Elem>()
|
|
86
|
+
for (const conn of memory.connections) {
|
|
87
|
+
if (conn.boundary && conn.boundary !== conn.source && conn.boundary !== conn.target) {
|
|
88
|
+
boundaries.add(conn.boundary)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const tree = treeFromMemoryState<Ctx>(memory, 'final')
|
|
92
|
+
const stage = memory.stageExclude({} as any)
|
|
93
|
+
|
|
94
|
+
const isRemovable = (el: Elem) =>
|
|
95
|
+
!(
|
|
96
|
+
boundaries.has(el)
|
|
97
|
+
|| memory.explicits.has(el)
|
|
98
|
+
|| tree.hasInOut(el)
|
|
99
|
+
|| tree.root.has(el)
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
const singleRoot = only([...tree.root])
|
|
103
|
+
if (singleRoot && !memory.explicits.has(singleRoot)) {
|
|
104
|
+
stage.exclude(singleRoot)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
for (const el of memory.final) {
|
|
108
|
+
const singleChild = only(tree.children(el))
|
|
109
|
+
if (singleChild && !tree.hasInOut(singleChild) && isRemovable(el)) {
|
|
110
|
+
stage.exclude(el)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (stage.isDirty()) {
|
|
115
|
+
return stage.commit()
|
|
116
|
+
}
|
|
117
|
+
return memory
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public commit(): Memory {
|
|
121
|
+
// return []
|
|
122
|
+
const step1 = this.step1CleanConnections(this.memory)
|
|
123
|
+
const step2 = this.step2ProcessImplicits(step1)
|
|
124
|
+
return this.step3ProcessBoundaries(step2)
|
|
125
|
+
// return step2memory
|
|
126
|
+
// const step3m?emory = this.step3FlatNodes(step2memory)
|
|
127
|
+
// return step3memory
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { dropWhile, forEach, pipe, take, zip } from 'remeda'
|
|
2
|
+
import { Expr } from '../../..'
|
|
3
|
+
import { findConnection, findConnectionsBetween } from '../../../model/connection/model'
|
|
4
|
+
import { difference, isAncestor, isIterable } from '../../../utils'
|
|
5
|
+
import { toArray } from '../../../utils/iterable'
|
|
6
|
+
import { type CtxConnection, type StageExpression, AbstractStageInclude } from '../../memory'
|
|
7
|
+
import { type ActiveGroupCtx, type ActiveGroupMemory, type Ctx } from './memory'
|
|
8
|
+
|
|
9
|
+
export type Elem = Ctx['Element']
|
|
10
|
+
|
|
11
|
+
export class StageInclude<C extends Ctx = Ctx> extends AbstractStageInclude<C> {
|
|
12
|
+
/**
|
|
13
|
+
* Connects elements with existing ones in the memory
|
|
14
|
+
*/
|
|
15
|
+
public override connectWithExisting(
|
|
16
|
+
elements: Elem | Iterable<Elem>,
|
|
17
|
+
direction: 'in' | 'out' | 'both' = 'both',
|
|
18
|
+
): boolean {
|
|
19
|
+
const before = this._connections.length
|
|
20
|
+
const hasChanged = () => this._connections.length > before
|
|
21
|
+
if (!isIterable(elements)) {
|
|
22
|
+
if (direction === 'in' || direction === 'both') {
|
|
23
|
+
for (const el of this.memory.elements) {
|
|
24
|
+
this.addConnections(
|
|
25
|
+
findConnection(el, elements, 'directed'),
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (direction === 'out' || direction === 'both') {
|
|
30
|
+
this.addConnections(
|
|
31
|
+
findConnectionsBetween(elements, this.memory.elements, 'directed'),
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
return hasChanged()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const targets = [...elements]
|
|
38
|
+
if (direction === 'in' || direction === 'both') {
|
|
39
|
+
for (const el of this.memory.elements) {
|
|
40
|
+
this.addConnections(
|
|
41
|
+
findConnectionsBetween(el, targets, 'directed'),
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (direction === 'out' || direction === 'both') {
|
|
46
|
+
for (const el of targets) {
|
|
47
|
+
this.addConnections(
|
|
48
|
+
findConnectionsBetween(el, this.memory.elements, 'directed'),
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return hasChanged()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
protected addImplicitWithinScope(element: Elem | undefined | null) {
|
|
56
|
+
if (!element) {
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
if (!this.memory.scope || isAncestor(this.memory.scope, element)) {
|
|
60
|
+
this.addImplicit(element)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
protected override processConnections(connections: CtxConnection<Ctx>[]) {
|
|
65
|
+
if (Expr.isRelationPredicateExpr(this.expression)) {
|
|
66
|
+
return connections
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
pipe(
|
|
70
|
+
connections,
|
|
71
|
+
forEach(({ source, target, boundary }) => {
|
|
72
|
+
pipe(
|
|
73
|
+
zip(
|
|
74
|
+
[...toArray(source.ancestors()).reverse(), source],
|
|
75
|
+
[...toArray(target.ancestors()).reverse(), target],
|
|
76
|
+
),
|
|
77
|
+
// Filter out common ancestors
|
|
78
|
+
dropWhile(([sourceAncestor, targetAncestor]) => sourceAncestor === targetAncestor),
|
|
79
|
+
take(1),
|
|
80
|
+
forEach(([sourceAncestor, targetAncestor]) => {
|
|
81
|
+
if (sourceAncestor === source && targetAncestor === target) {
|
|
82
|
+
this.addImplicitWithinScope(boundary)
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
if (sourceAncestor !== source) {
|
|
86
|
+
this.addImplicitWithinScope(sourceAncestor)
|
|
87
|
+
}
|
|
88
|
+
if (targetAncestor !== target) {
|
|
89
|
+
this.addImplicitWithinScope(targetAncestor)
|
|
90
|
+
}
|
|
91
|
+
}),
|
|
92
|
+
)
|
|
93
|
+
}),
|
|
94
|
+
)
|
|
95
|
+
return connections
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
protected override postcommit(state: ActiveGroupCtx['MutableState']) {
|
|
99
|
+
const newExplicits = difference(state.explicits, this.memory.explicits)
|
|
100
|
+
for (const explicit of newExplicits) {
|
|
101
|
+
state.explicitFirstSeenIn.set(explicit, '@root' as any)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return state
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export class ActiveGroupStageInclude extends StageInclude<ActiveGroupCtx> {
|
|
109
|
+
constructor(
|
|
110
|
+
public override readonly memory: ActiveGroupMemory,
|
|
111
|
+
public override readonly expression: StageExpression<ActiveGroupCtx>,
|
|
112
|
+
) {
|
|
113
|
+
super(memory, expression)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
protected override postcommit(state: ActiveGroupCtx['MutableState']) {
|
|
117
|
+
const newExplicits = difference(state.explicits, this.memory.explicits)
|
|
118
|
+
for (const explicit of newExplicits) {
|
|
119
|
+
state.explicitFirstSeenIn.set(explicit, this.memory.activeGroupId)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
for (const implicit of [...this.explicits, ...this.implicits]) {
|
|
123
|
+
state.lastSeenIn.set(implicit, this.memory.activeGroupId)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return state
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { anyPass } from 'remeda'
|
|
2
|
+
import { nonexhaustive } from '../../../errors'
|
|
3
|
+
import type { LikeC4Model } from '../../../model'
|
|
4
|
+
import * as Expr from '../../../types/expression'
|
|
5
|
+
import { isDescendantOf } from '../../../utils/fqn'
|
|
6
|
+
import { ifilter, toArray } from '../../../utils/iterable'
|
|
7
|
+
import type { Elem, Memory, PredicateCtx } from '../_types'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Resolve elements from the model based on the given expression
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export function resolveElements(model: LikeC4Model, expr: Exclude<Expr.ElementExpression, Expr.WildcardExpr>): Elem[] {
|
|
14
|
+
switch (true) {
|
|
15
|
+
case Expr.isExpandedElementExpr(expr): {
|
|
16
|
+
const element = model.element(expr.expanded)
|
|
17
|
+
return [
|
|
18
|
+
element,
|
|
19
|
+
...element.children(),
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
case Expr.isElementRef(expr): {
|
|
23
|
+
const element = model.element(expr.element)
|
|
24
|
+
let children
|
|
25
|
+
if (expr.isChildren) {
|
|
26
|
+
children = toArray(element.children())
|
|
27
|
+
} else if (expr.isDescendants) {
|
|
28
|
+
children = toArray(element.descendants())
|
|
29
|
+
}
|
|
30
|
+
return children && children.length > 0 ? children : [element]
|
|
31
|
+
}
|
|
32
|
+
case Expr.isElementKindExpr(expr):
|
|
33
|
+
return [...ifilter(model.elements(), el => {
|
|
34
|
+
return expr.isEqual === (el.kind === expr.elementKind)
|
|
35
|
+
})]
|
|
36
|
+
case Expr.isElementTagExpr(expr):
|
|
37
|
+
return [...ifilter(model.elements(), el => {
|
|
38
|
+
return expr.isEqual === el.tags.includes(expr.elementTag)
|
|
39
|
+
})]
|
|
40
|
+
default:
|
|
41
|
+
nonexhaustive(expr)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Include elements that are not in the given set but are descendants of the current set
|
|
47
|
+
* Consider the following example:
|
|
48
|
+
* a1.* -> b1
|
|
49
|
+
* If there are a1.a2.a3 and b1.b2 in the memory, but not connected yet - we connect them
|
|
50
|
+
*/
|
|
51
|
+
export function includeDescendantsFromMemory(elements: Elem[], memory: Memory): Elem[] {
|
|
52
|
+
if (memory.isEmpty() || elements.length === 0) {
|
|
53
|
+
return elements
|
|
54
|
+
}
|
|
55
|
+
const descedantsOf = anyPass(elements.map(e => isDescendantOf(e)))
|
|
56
|
+
const fromMemory = toArray(
|
|
57
|
+
ifilter(
|
|
58
|
+
memory.elements,
|
|
59
|
+
el => !elements.includes(el) && descedantsOf(el),
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
return [
|
|
63
|
+
...elements,
|
|
64
|
+
...fromMemory,
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Combination of `resolveElements` and `includeDescendantsFromMemory`
|
|
70
|
+
*/
|
|
71
|
+
export function resolveAndIncludeFromMemory(
|
|
72
|
+
nonWildcard: Expr.NonWilcard,
|
|
73
|
+
{ memory, model }: Pick<PredicateCtx, 'model' | 'memory'>,
|
|
74
|
+
): Elem[] {
|
|
75
|
+
const resolved = resolveElements(model, nonWildcard)
|
|
76
|
+
// We include from memory only if the expression is:
|
|
77
|
+
// - expanded element
|
|
78
|
+
// - element reference with or with children (but not descendants - they are already included)
|
|
79
|
+
if (Expr.isExpandedElementExpr(nonWildcard) || Expr.isElementRef(nonWildcard)) {
|
|
80
|
+
if (nonWildcard.isDescendants === true) {
|
|
81
|
+
return resolved
|
|
82
|
+
}
|
|
83
|
+
return includeDescendantsFromMemory(resolved, memory)
|
|
84
|
+
}
|
|
85
|
+
return resolved
|
|
86
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { findConnectionsWithin } from '../../../model/connection/model'
|
|
2
|
+
import type * as Expr from '../../../types/expression'
|
|
3
|
+
import type { Elem, PredicateExecutor } from '../_types'
|
|
4
|
+
|
|
5
|
+
export const ExpandedElementPredicate: PredicateExecutor<Expr.ExpandedElementExpr> = {
|
|
6
|
+
include: ({ expr, model, stage, where }) => {
|
|
7
|
+
const parent = model.element(expr.expanded)
|
|
8
|
+
if (where(parent)) {
|
|
9
|
+
stage.addExplicit(parent)
|
|
10
|
+
stage.connectWithExisting(parent)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const children = [...parent.children()].filter(where)
|
|
14
|
+
const expanded = [] as Elem[]
|
|
15
|
+
for (const child of children) {
|
|
16
|
+
stage.addImplicit(child)
|
|
17
|
+
if (stage.connectWithExisting(child)) {
|
|
18
|
+
expanded.push(child)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
stage.addConnections(findConnectionsWithin(expanded))
|
|
22
|
+
return stage
|
|
23
|
+
},
|
|
24
|
+
exclude: ({ expr, model, stage, where }) => {
|
|
25
|
+
const parent = model.element(expr.expanded)
|
|
26
|
+
const elements = [
|
|
27
|
+
parent,
|
|
28
|
+
...parent.children()
|
|
29
|
+
].filter(where)
|
|
30
|
+
stage.exclude(elements)
|
|
31
|
+
return stage
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { findConnectionsWithin } from '../../../model/connection/model'
|
|
2
|
+
import * as Expr from '../../../types/expression'
|
|
3
|
+
import type { PredicateExecutor } from '../_types'
|
|
4
|
+
import { resolveElements } from './_utils'
|
|
5
|
+
|
|
6
|
+
export const ElementKindOrTagPredicate: PredicateExecutor<Expr.ElementKindExpr | Expr.ElementTagExpr> = {
|
|
7
|
+
include: ({ expr, model, stage, filterWhere }) => {
|
|
8
|
+
const elements = filterWhere(resolveElements(model, expr))
|
|
9
|
+
if (elements.length === 0) {
|
|
10
|
+
return
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
stage.addExplicit(elements)
|
|
14
|
+
stage.connectWithExisting(elements)
|
|
15
|
+
stage.addConnections(findConnectionsWithin(elements))
|
|
16
|
+
|
|
17
|
+
return stage
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
exclude: ({ expr, model, stage, filterWhere }) => {
|
|
21
|
+
const elements = filterWhere(resolveElements(model, expr))
|
|
22
|
+
stage.exclude(elements)
|
|
23
|
+
|
|
24
|
+
return stage
|
|
25
|
+
}
|
|
26
|
+
}
|