@likec4/core 1.19.0 → 1.19.2
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/dist/builder/index.d.mts +3 -3
- package/dist/builder/index.d.ts +3 -3
- package/dist/builder/index.js +2 -2
- package/dist/compute-view/index.d.mts +2 -2
- package/dist/compute-view/index.d.ts +2 -2
- package/dist/compute-view/index.js +3 -3
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/model/index.d.mts +8 -3
- package/dist/model/index.d.ts +8 -3
- package/dist/model/index.js +1 -1
- package/dist/shared/{core.BZ9Msq7w.d.mts → core.2I-l5UtM.d.mts} +5 -1
- package/dist/shared/{core.CmYMqgha.d.mts → core.BL1oTk7-.d.mts} +13 -3
- package/dist/shared/{core.BIfgabEw.d.ts → core.CqXs9Frh.d.ts} +5 -1
- package/dist/shared/{core.C05RDLhi.d.ts → core.DyJ41fBO.d.ts} +13 -3
- package/dist/shared/{index.-BdzdI2C.js → index.KOeaSlL8.js} +333 -209
- package/dist/shared/{view.CyZrG8BJ.js → view.DrmaOfjr.js} +3 -0
- package/dist/types/index.d.mts +2 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.js +1 -1
- package/dist/utils/index.d.mts +16 -1
- package/dist/utils/index.d.ts +16 -1
- package/dist/utils/index.js +18 -1
- package/package.json +9 -9
- package/src/builder/Builder.view-common.ts +1 -0
- package/src/compute-view/deployment-view/__test__/fixture.ts +31 -3
- package/src/compute-view/deployment-view/_types.ts +2 -1
- package/src/compute-view/deployment-view/compute.ts +4 -36
- package/src/compute-view/deployment-view/predicates/index.ts +8 -0
- package/src/compute-view/deployment-view/predicates/relation-direct.ts +117 -88
- package/src/compute-view/deployment-view/predicates/relation-in-out.ts +16 -8
- package/src/compute-view/deployment-view/predicates/relation-incoming.ts +18 -12
- package/src/compute-view/deployment-view/predicates/relation-outgoing.ts +16 -9
- package/src/compute-view/deployment-view/predicates/relation-where.ts +18 -0
- package/src/compute-view/deployment-view/predicates/utils.ts +183 -0
- package/src/compute-view/memory/ops.ts +1 -1
- package/src/model/DeploymentElementModel.ts +14 -1
- package/src/model/RelationModel.ts +4 -0
- package/src/model/connection/deployment/DeploymentConnectionModel.ts +11 -1
- package/src/types/deployments.ts +1 -0
- package/src/types/expression-v2.ts +12 -0
- package/src/types/index.ts +1 -0
- package/src/types/operators.ts +5 -5
- package/src/utils/index.ts +1 -0
- package/src/utils/iterable/find.ts +36 -0
- package/src/utils/iterable/index.ts +1 -0
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { filter, flatMap, isNonNullish,
|
|
1
|
+
import { filter, flatMap, isNonNullish, pick, pipe } from 'remeda'
|
|
2
2
|
import { invariant } from '../../../errors'
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
ConnectionModel,
|
|
5
|
+
DeploymentElementModel,
|
|
6
|
+
DeploymentRelationModel,
|
|
7
|
+
LikeC4DeploymentModel,
|
|
8
|
+
} from '../../../model'
|
|
4
9
|
import type { DeploymentConnectionModel } from '../../../model/connection/deployment'
|
|
5
10
|
import { findConnection, findConnectionsBetween, findConnectionsWithin } from '../../../model/connection/deployment'
|
|
6
11
|
import { findConnectionsBetween as findModelConnectionsBetween } from '../../../model/connection/model'
|
|
7
|
-
import type { ConnectionModel } from '../../../model/connection/model/ConnectionModel'
|
|
8
|
-
import type { DeploymentElementModel } from '../../../model/DeploymentElementModel'
|
|
9
12
|
import type { RelationshipModel } from '../../../model/RelationModel'
|
|
10
13
|
import type { AnyAux } from '../../../model/types'
|
|
11
|
-
import { type RelationExpr, FqnExpr } from '../../../types'
|
|
12
|
-
import {
|
|
13
|
-
import type { ExcludePredicateCtx, PredicateExecutor } from '../_types'
|
|
14
|
-
import type { StageExclude } from '../memory'
|
|
14
|
+
import { type Filterable, type OperatorPredicate, type RelationExpr, FqnExpr } from '../../../types'
|
|
15
|
+
import type { PredicateExecutor } from '../_types'
|
|
15
16
|
import { deploymentExpressionToPredicate, resolveElements, resolveModelElements } from '../utils'
|
|
16
17
|
import { filterIncomingConnections, resolveAllImcomingRelations } from './relation-incoming'
|
|
17
18
|
import { filterOutgoingConnections, resolveAllOutgoingRelations } from './relation-outgoing'
|
|
18
|
-
|
|
19
|
-
// const isRef = DeploymentElementExpression.isRef
|
|
19
|
+
import { applyPredicate, excludeModelRelations } from './utils'
|
|
20
20
|
|
|
21
21
|
export const resolveAscendingSiblings = (element: DeploymentElementModel) => {
|
|
22
22
|
const siblings = new Set<DeploymentElementModel>()
|
|
@@ -40,7 +40,7 @@ const resolveWildcard = (model: LikeC4DeploymentModel, nonWildcard: FqnExpr.Depl
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export const DirectRelationPredicate: PredicateExecutor<RelationExpr.Direct> = {
|
|
43
|
-
include: ({ expr: { source, target, isBidirectional = false }, model, stage }) => {
|
|
43
|
+
include: ({ expr: { source, target, isBidirectional = false }, model, stage, where }) => {
|
|
44
44
|
invariant(!FqnExpr.isModelRef(source), 'Invalid source model ref in direct relation')
|
|
45
45
|
invariant(!FqnExpr.isModelRef(target), 'Invalid target model ref in direct relation')
|
|
46
46
|
const sourceIsWildcard = FqnExpr.isWildcard(source)
|
|
@@ -109,7 +109,7 @@ export const DirectRelationPredicate: PredicateExecutor<RelationExpr.Direct> = {
|
|
|
109
109
|
|
|
110
110
|
connections = pipe(
|
|
111
111
|
sources,
|
|
112
|
-
flatMap(s => findConnectionsBetween(s, targets, dir)),
|
|
112
|
+
flatMap(s => matchConnections(findConnectionsBetween(s, targets, dir), where)),
|
|
113
113
|
filter(c => isSource(c) && isTarget(c)),
|
|
114
114
|
)
|
|
115
115
|
}
|
|
@@ -126,90 +126,72 @@ export const DirectRelationPredicate: PredicateExecutor<RelationExpr.Direct> = {
|
|
|
126
126
|
|
|
127
127
|
return stage
|
|
128
128
|
},
|
|
129
|
-
exclude: ({ expr, model, memory, stage }) => {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (
|
|
133
|
-
FqnExpr.isWildcard(expr.source)
|
|
134
|
-
&& FqnExpr.isWildcard(expr.target)
|
|
135
|
-
) {
|
|
136
|
-
stage.excludeConnections(memory.connections)
|
|
137
|
-
return stage
|
|
138
|
-
}
|
|
129
|
+
exclude: ({ expr, model, memory, stage, where }) => {
|
|
130
|
+
const isTarget = deploymentExpressionToPredicate(expr.target)
|
|
131
|
+
const isSource = deploymentExpressionToPredicate(expr.source)
|
|
139
132
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
})
|
|
147
|
-
return excludeModelRelations(toExclude, { stage, memory })
|
|
148
|
-
}
|
|
133
|
+
let modelRelationsToExclude: ReadonlySet<RelationshipModel<AnyAux>>
|
|
134
|
+
switch (true) {
|
|
135
|
+
// * -> *
|
|
136
|
+
case FqnExpr.isWildcard(expr.source) && FqnExpr.isWildcard(expr.target):
|
|
137
|
+
stage.excludeConnections(matchConnections(memory.connections, where))
|
|
138
|
+
return stage
|
|
149
139
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
{ stage, memory },
|
|
160
|
-
c => isTarget(c.target),
|
|
161
|
-
)
|
|
162
|
-
}
|
|
140
|
+
// model -> model
|
|
141
|
+
case FqnExpr.isModelRef(expr.source) && FqnExpr.isModelRef(expr.target):
|
|
142
|
+
modelRelationsToExclude = resolveRelationsBetweenModelElements({
|
|
143
|
+
source: expr.source,
|
|
144
|
+
target: expr.target,
|
|
145
|
+
expr,
|
|
146
|
+
model,
|
|
147
|
+
})
|
|
148
|
+
return excludeModelRelations(modelRelationsToExclude, { stage, memory }, where)
|
|
163
149
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
toExclude,
|
|
169
|
-
{ stage, memory },
|
|
170
|
-
c => isSource(c.source),
|
|
171
|
-
)
|
|
172
|
-
}
|
|
150
|
+
// model -> *
|
|
151
|
+
case FqnExpr.isModelRef(expr.source) && FqnExpr.isWildcard(expr.target):
|
|
152
|
+
modelRelationsToExclude = resolveAllOutgoingRelations(model, expr.source)
|
|
153
|
+
return excludeModelRelations(modelRelationsToExclude, { stage, memory }, where)
|
|
173
154
|
|
|
174
|
-
|
|
175
|
-
|
|
155
|
+
// model -> deployment
|
|
156
|
+
case FqnExpr.isModelRef(expr.source):
|
|
157
|
+
modelRelationsToExclude = resolveAllOutgoingRelations(model, expr.source)
|
|
158
|
+
return excludeModelRelations(
|
|
159
|
+
modelRelationsToExclude,
|
|
160
|
+
{ stage, memory },
|
|
161
|
+
where,
|
|
162
|
+
c => isTarget(c.target),
|
|
163
|
+
)
|
|
176
164
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
165
|
+
// deployment -> model
|
|
166
|
+
case FqnExpr.isModelRef(expr.target):
|
|
167
|
+
modelRelationsToExclude = resolveAllImcomingRelations(model, expr.target)
|
|
168
|
+
return excludeModelRelations(
|
|
169
|
+
modelRelationsToExclude,
|
|
170
|
+
{ stage, memory },
|
|
171
|
+
where,
|
|
172
|
+
c => isSource(c.source),
|
|
173
|
+
)
|
|
181
174
|
|
|
182
|
-
|
|
175
|
+
// deployment -> deployment
|
|
176
|
+
default:
|
|
177
|
+
const satisfies = (connection: DeploymentConnectionModel) => {
|
|
178
|
+
return (isSource(connection.source) && isTarget(connection.target))
|
|
179
|
+
|| (expr.isBidirectional === true && isSource(connection.target) && isTarget(connection.source))
|
|
180
|
+
}
|
|
183
181
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
182
|
+
const deploymentRelationsToExclude = pipe(
|
|
183
|
+
memory.connections,
|
|
184
|
+
filter(satisfies),
|
|
185
|
+
applyPredicate(where),
|
|
186
|
+
)
|
|
187
|
+
if (deploymentRelationsToExclude.length === 0) {
|
|
188
|
+
return stage
|
|
189
|
+
}
|
|
188
190
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
filterConnections: (c: DeploymentConnectionModel) => boolean = () => true,
|
|
194
|
-
): StageExclude {
|
|
195
|
-
if (relationsToExclude.size === 0) {
|
|
196
|
-
return stage
|
|
197
|
-
}
|
|
198
|
-
const toExclude = pipe(
|
|
199
|
-
memory.connections,
|
|
200
|
-
// Find connections that have at least one relation in common with the excluded relations
|
|
201
|
-
filter(c => filterConnections(c) && hasIntersection(c.relations.model, relationsToExclude)),
|
|
202
|
-
map(c =>
|
|
203
|
-
c.update({
|
|
204
|
-
deployment: null,
|
|
205
|
-
model: intersection(c.relations.model, relationsToExclude),
|
|
206
|
-
})
|
|
207
|
-
),
|
|
208
|
-
)
|
|
209
|
-
if (toExclude.length === 0) {
|
|
210
|
-
return stage
|
|
211
|
-
}
|
|
212
|
-
return stage.excludeConnections(toExclude)
|
|
191
|
+
stage.excludeConnections(deploymentRelationsToExclude)
|
|
192
|
+
return stage
|
|
193
|
+
}
|
|
194
|
+
},
|
|
213
195
|
}
|
|
214
196
|
|
|
215
197
|
function resolveRelationsBetweenModelElements({
|
|
@@ -235,3 +217,50 @@ function resolveRelationsBetweenModelElements({
|
|
|
235
217
|
|
|
236
218
|
return new Set(modelConnections.flatMap(c => [...c.relations]))
|
|
237
219
|
}
|
|
220
|
+
|
|
221
|
+
function elementToFilterable<M extends AnyAux>(element: DeploymentElementModel<M>) {
|
|
222
|
+
return pick(element, ['tags', 'kind'])
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function toFilterableRelation<M extends AnyAux>(
|
|
226
|
+
source: DeploymentElementModel<M>,
|
|
227
|
+
target: DeploymentElementModel<M>,
|
|
228
|
+
) {
|
|
229
|
+
return (
|
|
230
|
+
relation: RelationshipModel<M> | DeploymentRelationModel<M>,
|
|
231
|
+
) => ({
|
|
232
|
+
tags: relation.tags,
|
|
233
|
+
kind: relation.kind,
|
|
234
|
+
source: elementToFilterable(source),
|
|
235
|
+
target: elementToFilterable(target),
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function matchConnection<M extends AnyAux>(
|
|
240
|
+
c: DeploymentConnectionModel<M>,
|
|
241
|
+
where: OperatorPredicate<Filterable> | null,
|
|
242
|
+
): boolean {
|
|
243
|
+
if (!where) {
|
|
244
|
+
return true
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return [
|
|
248
|
+
...Array.from(c.relations.deployment.values()).map(toFilterableRelation(c.source, c.target)),
|
|
249
|
+
...Array.from(c.relations.model.values()).map(toFilterableRelation(c.source, c.target)),
|
|
250
|
+
]
|
|
251
|
+
.filter(where).length > 0
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function matchConnections<M extends AnyAux>(
|
|
255
|
+
connections: readonly DeploymentConnectionModel<M>[],
|
|
256
|
+
where: OperatorPredicate<Filterable> | null,
|
|
257
|
+
): readonly DeploymentConnectionModel[] {
|
|
258
|
+
if (!where) {
|
|
259
|
+
return connections
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return pipe(
|
|
263
|
+
connections,
|
|
264
|
+
filter(c => matchConnection(c, where)),
|
|
265
|
+
)
|
|
266
|
+
}
|
|
@@ -1,24 +1,27 @@
|
|
|
1
|
+
import { filter, pipe } from 'remeda'
|
|
1
2
|
import { invariant } from '../../../errors'
|
|
2
3
|
import { findConnectionsBetween } from '../../../model/connection/deployment'
|
|
3
4
|
import type { RelationshipModel } from '../../../model/RelationModel'
|
|
4
5
|
import type { AnyAux } from '../../../model/types'
|
|
5
|
-
import {
|
|
6
|
+
import { type RelationExpr, FqnExpr } from '../../../types/expression-v2'
|
|
6
7
|
import { union } from '../../../utils/set'
|
|
7
8
|
import type { PredicateExecutor } from '../_types'
|
|
8
9
|
import { resolveElements, resolveModelElements } from '../utils'
|
|
9
|
-
import {
|
|
10
|
+
import { resolveAscendingSiblings } from './relation-direct'
|
|
10
11
|
import { filterIncomingConnections } from './relation-incoming'
|
|
11
12
|
import { filterOutgoingConnections } from './relation-outgoing'
|
|
13
|
+
import { applyPredicate, excludeModelRelations, matchConnections } from './utils'
|
|
12
14
|
|
|
13
15
|
//
|
|
14
16
|
export const InOutRelationPredicate: PredicateExecutor<RelationExpr.InOut> = {
|
|
15
|
-
include: ({ expr, model, memory, stage }) => {
|
|
17
|
+
include: ({ expr, model, memory, stage, where }) => {
|
|
16
18
|
const sources = [...memory.elements]
|
|
17
19
|
|
|
18
20
|
if (FqnExpr.isWildcard(expr.inout)) {
|
|
19
21
|
for (const source of sources) {
|
|
20
22
|
const targets = [...resolveAscendingSiblings(source)]
|
|
21
|
-
|
|
23
|
+
const toInclude = matchConnections(findConnectionsBetween(source, targets, 'both'), where)
|
|
24
|
+
stage.addConnections(toInclude)
|
|
22
25
|
}
|
|
23
26
|
return stage
|
|
24
27
|
}
|
|
@@ -26,12 +29,13 @@ export const InOutRelationPredicate: PredicateExecutor<RelationExpr.InOut> = {
|
|
|
26
29
|
|
|
27
30
|
const targets = resolveElements(model, expr.inout)
|
|
28
31
|
for (const source of sources) {
|
|
29
|
-
|
|
32
|
+
const toInclude = matchConnections(findConnectionsBetween(source, targets, 'both'), where)
|
|
33
|
+
stage.addConnections(toInclude)
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
return stage
|
|
33
37
|
},
|
|
34
|
-
exclude: ({ expr, model, memory, stage }) => {
|
|
38
|
+
exclude: ({ expr, model, memory, stage, where }) => {
|
|
35
39
|
// Exclude all connections that have model relationshps with the elements
|
|
36
40
|
if (FqnExpr.isModelRef(expr.inout)) {
|
|
37
41
|
const elements = resolveModelElements(model, expr.inout)
|
|
@@ -42,7 +46,7 @@ export const InOutRelationPredicate: PredicateExecutor<RelationExpr.InOut> = {
|
|
|
42
46
|
new Set<RelationshipModel<AnyAux>>(),
|
|
43
47
|
...elements.flatMap(e => [e.allIncoming, e.allOutgoing]),
|
|
44
48
|
)
|
|
45
|
-
return excludeModelRelations(excludedRelations, { stage, memory })
|
|
49
|
+
return excludeModelRelations(excludedRelations, { stage, memory }, where)
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
if (FqnExpr.isWildcard(expr.inout)) {
|
|
@@ -54,7 +58,11 @@ export const InOutRelationPredicate: PredicateExecutor<RelationExpr.InOut> = {
|
|
|
54
58
|
const isIncoming = filterIncomingConnections(elements)
|
|
55
59
|
const isOutgoing = filterOutgoingConnections(elements)
|
|
56
60
|
|
|
57
|
-
const toExclude =
|
|
61
|
+
const toExclude = pipe(
|
|
62
|
+
memory.connections,
|
|
63
|
+
filter(c => isIncoming(c) !== isOutgoing(c)),
|
|
64
|
+
applyPredicate(where),
|
|
65
|
+
)
|
|
58
66
|
stage.excludeConnections(toExclude)
|
|
59
67
|
return stage
|
|
60
68
|
},
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { anyPass } from 'remeda'
|
|
1
|
+
import { anyPass, filter, pipe } from 'remeda'
|
|
2
2
|
import { invariant } from '../../../errors'
|
|
3
|
+
import type { AnyAux, RelationshipModel } from '../../../model'
|
|
3
4
|
import { findConnectionsBetween } from '../../../model/connection/deployment'
|
|
4
5
|
import type { LikeC4DeploymentModel } from '../../../model/DeploymentModel'
|
|
5
|
-
import type
|
|
6
|
-
import
|
|
7
|
-
import { FqnExpr, type RelationExpr } from '../../../types'
|
|
8
|
-
import { isAncestor } from '../../../utils/fqn'
|
|
6
|
+
import { type RelationExpr, FqnExpr } from '../../../types'
|
|
7
|
+
import { isAncestor } from '../../../utils'
|
|
9
8
|
import type { Connection, Elem, PredicateExecutor } from '../_types'
|
|
10
9
|
import { resolveElements, resolveModelElements } from '../utils'
|
|
11
|
-
import {
|
|
10
|
+
import { resolveAscendingSiblings } from './relation-direct'
|
|
11
|
+
import { applyPredicate, excludeModelRelations, matchConnection, matchConnections } from './utils'
|
|
12
12
|
|
|
13
13
|
// from visible element incoming to this
|
|
14
14
|
export const IncomingRelationPredicate: PredicateExecutor<RelationExpr.Incoming> = {
|
|
15
|
-
include: ({ expr, model, memory, stage }) => {
|
|
15
|
+
include: ({ expr, model, memory, stage, where }) => {
|
|
16
16
|
const sources = [...memory.elements]
|
|
17
17
|
|
|
18
18
|
if (FqnExpr.isWildcard(expr.incoming)) {
|
|
@@ -21,7 +21,8 @@ export const IncomingRelationPredicate: PredicateExecutor<RelationExpr.Incoming>
|
|
|
21
21
|
continue
|
|
22
22
|
}
|
|
23
23
|
const targets = [...resolveAscendingSiblings(source)]
|
|
24
|
-
|
|
24
|
+
const toInclude = applyPredicate(findConnectionsBetween(source, targets, 'directed'), where)
|
|
25
|
+
stage.addConnections(toInclude)
|
|
25
26
|
}
|
|
26
27
|
return stage
|
|
27
28
|
}
|
|
@@ -29,16 +30,17 @@ export const IncomingRelationPredicate: PredicateExecutor<RelationExpr.Incoming>
|
|
|
29
30
|
|
|
30
31
|
const targets = resolveElements(model, expr.incoming)
|
|
31
32
|
for (const source of sources) {
|
|
32
|
-
|
|
33
|
+
const toInclude = applyPredicate(findConnectionsBetween(source, targets, 'directed'), where)
|
|
34
|
+
stage.addConnections(toInclude)
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
return stage
|
|
36
38
|
},
|
|
37
|
-
exclude: ({ expr, model, memory, stage }) => {
|
|
39
|
+
exclude: ({ expr, model, memory, stage, where }) => {
|
|
38
40
|
// Exclude all connections that have model relationshps with the elements
|
|
39
41
|
if (FqnExpr.isModelRef(expr.incoming)) {
|
|
40
42
|
const excludedRelations = resolveAllImcomingRelations(model, expr.incoming)
|
|
41
|
-
return excludeModelRelations(excludedRelations, { stage, memory })
|
|
43
|
+
return excludeModelRelations(excludedRelations, { stage, memory }, where)
|
|
42
44
|
}
|
|
43
45
|
if (FqnExpr.isWildcard(expr.incoming)) {
|
|
44
46
|
// non-sense
|
|
@@ -46,7 +48,11 @@ export const IncomingRelationPredicate: PredicateExecutor<RelationExpr.Incoming>
|
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
const isIncoming = filterIncomingConnections(resolveElements(model, expr.incoming))
|
|
49
|
-
const toExclude =
|
|
51
|
+
const toExclude = pipe(
|
|
52
|
+
memory.connections,
|
|
53
|
+
filter(isIncoming),
|
|
54
|
+
applyPredicate(where),
|
|
55
|
+
)
|
|
50
56
|
stage.excludeConnections(toExclude)
|
|
51
57
|
return stage
|
|
52
58
|
},
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { anyPass } from 'remeda'
|
|
1
|
+
import { anyPass, filter, pipe } from 'remeda'
|
|
2
2
|
import { invariant } from '../../../errors'
|
|
3
3
|
import type { LikeC4DeploymentModel } from '../../../model'
|
|
4
4
|
import { findConnection, findConnectionsBetween } from '../../../model/connection/deployment'
|
|
5
5
|
import type { RelationshipModel } from '../../../model/RelationModel'
|
|
6
|
-
import {
|
|
6
|
+
import { type RelationExpr, FqnExpr } from '../../../types'
|
|
7
7
|
import { isAncestor } from '../../../utils'
|
|
8
8
|
import type { Connection, Elem, PredicateExecutor } from '../_types'
|
|
9
9
|
import { resolveElements, resolveModelElements } from '../utils'
|
|
10
|
-
import {
|
|
10
|
+
import { resolveAscendingSiblings } from './relation-direct'
|
|
11
|
+
import { applyPredicate, excludeModelRelations } from './utils'
|
|
11
12
|
|
|
12
13
|
export const OutgoingRelationPredicate: PredicateExecutor<RelationExpr.Outgoing> = {
|
|
13
|
-
include: ({ expr, model, memory, stage }) => {
|
|
14
|
+
include: ({ expr, model, memory, stage, where }) => {
|
|
14
15
|
const targets = [...memory.elements]
|
|
15
16
|
|
|
16
17
|
// * -> (to visible elements)
|
|
@@ -21,7 +22,8 @@ export const OutgoingRelationPredicate: PredicateExecutor<RelationExpr.Outgoing>
|
|
|
21
22
|
continue
|
|
22
23
|
}
|
|
23
24
|
for (const source of resolveAscendingSiblings(target)) {
|
|
24
|
-
|
|
25
|
+
const toInclude = applyPredicate(findConnection(source, target, 'directed'), where)
|
|
26
|
+
stage.addConnections(toInclude)
|
|
25
27
|
}
|
|
26
28
|
}
|
|
27
29
|
return stage
|
|
@@ -30,16 +32,17 @@ export const OutgoingRelationPredicate: PredicateExecutor<RelationExpr.Outgoing>
|
|
|
30
32
|
|
|
31
33
|
const sources = resolveElements(model, expr.outgoing)
|
|
32
34
|
for (const source of sources) {
|
|
33
|
-
|
|
35
|
+
const toInclude = applyPredicate(findConnectionsBetween(source, targets, 'directed'), where)
|
|
36
|
+
stage.addConnections(toInclude)
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
return stage
|
|
37
40
|
},
|
|
38
|
-
exclude: ({ expr, model, memory, stage }) => {
|
|
41
|
+
exclude: ({ expr, model, memory, stage, where }) => {
|
|
39
42
|
// Exclude all connections that have model relationshps with the elements
|
|
40
43
|
if (FqnExpr.isModelRef(expr.outgoing)) {
|
|
41
44
|
const excludedRelations = resolveAllOutgoingRelations(model, expr.outgoing)
|
|
42
|
-
return excludeModelRelations(excludedRelations, { stage, memory })
|
|
45
|
+
return excludeModelRelations(excludedRelations, { stage, memory }, where)
|
|
43
46
|
}
|
|
44
47
|
if (FqnExpr.isWildcard(expr.outgoing)) {
|
|
45
48
|
// non-sense
|
|
@@ -47,7 +50,11 @@ export const OutgoingRelationPredicate: PredicateExecutor<RelationExpr.Outgoing>
|
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
const isOutgoing = filterOutgoingConnections(resolveElements(model, expr.outgoing))
|
|
50
|
-
const toExclude =
|
|
53
|
+
const toExclude = pipe(
|
|
54
|
+
memory.connections,
|
|
55
|
+
filter(isOutgoing),
|
|
56
|
+
applyPredicate(where),
|
|
57
|
+
)
|
|
51
58
|
stage.excludeConnections(toExclude)
|
|
52
59
|
return stage
|
|
53
60
|
},
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { whereOperatorAsPredicate, type RelationExpr } from '../../../types'
|
|
2
|
+
import type { PredicateExecutor } from '../_types'
|
|
3
|
+
import type { StageExclude, StageInclude } from '../memory'
|
|
4
|
+
import { predicateToPatch } from './utils'
|
|
5
|
+
|
|
6
|
+
// relation matches the condition
|
|
7
|
+
export const WhereRelationPredicate: PredicateExecutor<RelationExpr.Where> = {
|
|
8
|
+
include: ({ expr, model, memory, stage }) => {
|
|
9
|
+
const where = whereOperatorAsPredicate(expr.where.condition)
|
|
10
|
+
|
|
11
|
+
return predicateToPatch('include', { expr: expr.where.expr, model, stage, memory, where }) as StageInclude
|
|
12
|
+
},
|
|
13
|
+
exclude: ({ expr, model, memory, stage }) => {
|
|
14
|
+
const where = whereOperatorAsPredicate(expr.where.condition)
|
|
15
|
+
|
|
16
|
+
return predicateToPatch('exclude', { expr: expr.where.expr, model, stage, memory, where }) as StageExclude
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { filter, isArray, map, pick, pipe } from 'remeda'
|
|
2
|
+
import { nonexhaustive } from '../../../errors'
|
|
3
|
+
import type { DeploymentConnectionModel } from '../../../model'
|
|
4
|
+
import type { DeploymentElementModel, DeploymentRelationModel } from '../../../model/DeploymentElementModel'
|
|
5
|
+
import type { RelationshipModel } from '../../../model/RelationModel'
|
|
6
|
+
import type { AnyAux } from '../../../model/types'
|
|
7
|
+
import { type Filterable, type OperatorPredicate, FqnExpr, RelationExpr } from '../../../types'
|
|
8
|
+
import { hasIntersection, intersection } from '../../../utils/set'
|
|
9
|
+
import type { ExcludePredicateCtx, PredicateCtx } from '../_types'
|
|
10
|
+
import type { StageExclude, StageInclude } from '../memory'
|
|
11
|
+
import { DeploymentRefPredicate } from './elements'
|
|
12
|
+
import { DirectRelationPredicate } from './relation-direct'
|
|
13
|
+
import { InOutRelationPredicate } from './relation-in-out'
|
|
14
|
+
import { IncomingRelationPredicate } from './relation-incoming'
|
|
15
|
+
import { OutgoingRelationPredicate } from './relation-outgoing'
|
|
16
|
+
import { WhereRelationPredicate } from './relation-where'
|
|
17
|
+
import { WildcardPredicate } from './wildcard'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Builds a patch object from an expression
|
|
21
|
+
*/
|
|
22
|
+
export function predicateToPatch(
|
|
23
|
+
op: 'include' | 'exclude',
|
|
24
|
+
{ expr, where, ...ctx }: PredicateCtx,
|
|
25
|
+
): StageExclude | StageInclude | undefined {
|
|
26
|
+
switch (true) {
|
|
27
|
+
case FqnExpr.isModelRef(expr):
|
|
28
|
+
// Ignore model refs in deployment view
|
|
29
|
+
return undefined
|
|
30
|
+
case FqnExpr.isDeploymentRef(expr):
|
|
31
|
+
return DeploymentRefPredicate[op]({ ...ctx, expr, where } as any)
|
|
32
|
+
case FqnExpr.isWildcard(expr):
|
|
33
|
+
return WildcardPredicate[op]({ ...ctx, expr, where } as any)
|
|
34
|
+
case RelationExpr.isDirect(expr):
|
|
35
|
+
return DirectRelationPredicate[op]({ ...ctx, expr, where } as any)
|
|
36
|
+
case RelationExpr.isInOut(expr):
|
|
37
|
+
return InOutRelationPredicate[op]({ ...ctx, expr, where } as any)
|
|
38
|
+
case RelationExpr.isOutgoing(expr):
|
|
39
|
+
return OutgoingRelationPredicate[op]({ ...ctx, expr, where } as any)
|
|
40
|
+
case RelationExpr.isIncoming(expr):
|
|
41
|
+
return IncomingRelationPredicate[op]({ ...ctx, expr, where } as any)
|
|
42
|
+
case RelationExpr.isWhere(expr):
|
|
43
|
+
return WhereRelationPredicate[op]({ ...ctx, expr, where } as any)
|
|
44
|
+
default:
|
|
45
|
+
nonexhaustive(expr)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function excludeModelRelations(
|
|
50
|
+
relationsToExclude: ReadonlySet<RelationshipModel<AnyAux>>,
|
|
51
|
+
{ stage, memory }: Pick<ExcludePredicateCtx, 'stage' | 'memory'>,
|
|
52
|
+
where: OperatorPredicate<Filterable> | null,
|
|
53
|
+
// Optional filter to scope the connections to exclude
|
|
54
|
+
filterConnections: (c: DeploymentConnectionModel) => boolean = () => true,
|
|
55
|
+
): StageExclude {
|
|
56
|
+
if (relationsToExclude.size === 0) {
|
|
57
|
+
return stage
|
|
58
|
+
}
|
|
59
|
+
const toExclude = pipe(
|
|
60
|
+
memory.connections,
|
|
61
|
+
filter(c => filterConnections(c)),
|
|
62
|
+
// Find connections that have at least one relation in common with the excluded relations
|
|
63
|
+
filter(c => hasIntersection(c.relations.model, relationsToExclude)),
|
|
64
|
+
map(c =>
|
|
65
|
+
c.update({
|
|
66
|
+
deployment: null,
|
|
67
|
+
model: intersection(c.relations.model, relationsToExclude),
|
|
68
|
+
})
|
|
69
|
+
),
|
|
70
|
+
applyPredicate(where),
|
|
71
|
+
filter(c => c.nonEmpty()),
|
|
72
|
+
)
|
|
73
|
+
if (toExclude.length === 0) {
|
|
74
|
+
return stage
|
|
75
|
+
}
|
|
76
|
+
return stage.excludeConnections(toExclude)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function matchConnection<M extends AnyAux>(
|
|
80
|
+
c: DeploymentConnectionModel<M>,
|
|
81
|
+
where: OperatorPredicate<Filterable> | null,
|
|
82
|
+
): boolean {
|
|
83
|
+
return applyPredicate(c, where).nonEmpty()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Filters relations of the provided connections using the provided predicate.
|
|
88
|
+
* And copy of the connection with the filtered relations will be created.
|
|
89
|
+
* Connections left without relations are removed from resulting collection.
|
|
90
|
+
*
|
|
91
|
+
* @param c The connection to apply the predicate to
|
|
92
|
+
* @param where The predicate
|
|
93
|
+
* @returns A copy of the connection with the filtered relations
|
|
94
|
+
*/
|
|
95
|
+
export function applyPredicate<M extends AnyAux>(
|
|
96
|
+
c: readonly DeploymentConnectionModel<M>[],
|
|
97
|
+
where: OperatorPredicate<Filterable> | null,
|
|
98
|
+
): readonly DeploymentConnectionModel<M>[]
|
|
99
|
+
/**
|
|
100
|
+
* Filters relations of the connection using the provided predicate.
|
|
101
|
+
*
|
|
102
|
+
* @param c The connection to apply the predicate to
|
|
103
|
+
* @param where The predicate
|
|
104
|
+
* @returns A deep copy of the original collection with the filtered relations
|
|
105
|
+
*/
|
|
106
|
+
export function applyPredicate<M extends AnyAux>(
|
|
107
|
+
c: DeploymentConnectionModel<M>,
|
|
108
|
+
where: OperatorPredicate<Filterable> | null,
|
|
109
|
+
): DeploymentConnectionModel<M>
|
|
110
|
+
/**
|
|
111
|
+
* Creates a function that filters relations of the provided connections using the provided predicate.
|
|
112
|
+
* Connections left without relations are removed from resulting collection.
|
|
113
|
+
*
|
|
114
|
+
* @param c The connection to apply the predicate to
|
|
115
|
+
* @param where The predicate
|
|
116
|
+
* @returns A function to create a filtered copy of connections
|
|
117
|
+
*/
|
|
118
|
+
export function applyPredicate<M extends AnyAux>(
|
|
119
|
+
where: OperatorPredicate<Filterable> | null,
|
|
120
|
+
): (data: readonly DeploymentConnectionModel<M>[]) => readonly DeploymentConnectionModel<M>[]
|
|
121
|
+
export function applyPredicate<M extends AnyAux>(
|
|
122
|
+
...args:
|
|
123
|
+
| [readonly DeploymentConnectionModel<M>[], OperatorPredicate<Filterable> | null]
|
|
124
|
+
| [OperatorPredicate<Filterable> | null]
|
|
125
|
+
| [c: DeploymentConnectionModel<M>, OperatorPredicate<Filterable> | null]
|
|
126
|
+
):
|
|
127
|
+
| DeploymentConnectionModel<M>
|
|
128
|
+
| readonly DeploymentConnectionModel<M>[]
|
|
129
|
+
| ((data: readonly DeploymentConnectionModel<M>[]) => readonly DeploymentConnectionModel<M>[])
|
|
130
|
+
{
|
|
131
|
+
if (args.length === 1) {
|
|
132
|
+
return x => applyPredicate(x, args[0])
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const [c, where] = args
|
|
136
|
+
if (where === null) {
|
|
137
|
+
return c
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (isArray(c)) {
|
|
141
|
+
return c
|
|
142
|
+
.map(x => applyPredicate(x, where))
|
|
143
|
+
.filter(x => x.nonEmpty())
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const map = toFilterableRelation(c.source, c.target)
|
|
147
|
+
return c.update({
|
|
148
|
+
model: new Set([...c.relations.model.values()].filter(r => where(map(r)))),
|
|
149
|
+
deployment: new Set([...c.relations.deployment.values()].filter(r => where(map(r)))),
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function matchConnections<M extends AnyAux>(
|
|
154
|
+
connections: readonly DeploymentConnectionModel<M>[],
|
|
155
|
+
where: OperatorPredicate<Filterable> | null,
|
|
156
|
+
): readonly DeploymentConnectionModel[] {
|
|
157
|
+
if (!where) {
|
|
158
|
+
return connections
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return pipe(
|
|
162
|
+
connections,
|
|
163
|
+
filter(c => matchConnection(c, where)),
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function elementToFilterable<M extends AnyAux>(element: DeploymentElementModel<M>) {
|
|
168
|
+
return pick(element, ['tags', 'kind'])
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function toFilterableRelation<M extends AnyAux>(
|
|
172
|
+
source: DeploymentElementModel<M>,
|
|
173
|
+
target: DeploymentElementModel<M>,
|
|
174
|
+
) {
|
|
175
|
+
return (
|
|
176
|
+
relation: RelationshipModel<M> | DeploymentRelationModel<M>,
|
|
177
|
+
) => ({
|
|
178
|
+
tags: relation.tags,
|
|
179
|
+
kind: relation.kind,
|
|
180
|
+
source: elementToFilterable(source),
|
|
181
|
+
target: elementToFilterable(target),
|
|
182
|
+
})
|
|
183
|
+
}
|
|
@@ -37,7 +37,7 @@ export function treeFromMemoryState<T extends AnyCtx, M extends ComputeMemory<T>
|
|
|
37
37
|
root: root as ReadonlySet<CtxElement<T>>,
|
|
38
38
|
connected: connected as ReadonlySet<CtxElement<T>>,
|
|
39
39
|
hasInOut: (el: CtxElement<T>) => memory.connections.some(Connection.isAnyInOut(el.id)),
|
|
40
|
-
|
|
40
|
+
parent: (el: CtxElement<T>) => parents.get(el),
|
|
41
41
|
children: (el: CtxElement<T>) => children.get(el),
|
|
42
42
|
}
|
|
43
43
|
}
|