@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,178 @@
|
|
|
1
|
+
import { invariant } from '../../../errors'
|
|
2
|
+
import type { IteratorLike } from '../../../types'
|
|
3
|
+
import { stringHash } from '../../../utils'
|
|
4
|
+
import { customInspectSymbol } from '../../../utils/const'
|
|
5
|
+
import { equals } from '../../../utils/set'
|
|
6
|
+
import {
|
|
7
|
+
type DeployedInstanceModel,
|
|
8
|
+
type DeploymentElementModel,
|
|
9
|
+
type DeploymentRelationModel,
|
|
10
|
+
DeploymentNodeModel,
|
|
11
|
+
RelationshipsAccum,
|
|
12
|
+
} from '../../DeploymentElementModel'
|
|
13
|
+
import type { RelationshipModel } from '../../RelationModel'
|
|
14
|
+
import type { AnyAux } from '../../types'
|
|
15
|
+
import type { Connection } from '../Connection'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Connection is ephemeral entity, result of a resolving relationships between source and target.
|
|
19
|
+
* Includes direct relationships and/or between their nested elements.
|
|
20
|
+
*/
|
|
21
|
+
export class DeploymentConnectionModel<M extends AnyAux = AnyAux>
|
|
22
|
+
implements Connection<DeploymentElementModel<M>, M['EdgeId']>
|
|
23
|
+
{
|
|
24
|
+
constructor(
|
|
25
|
+
public readonly source: DeploymentNodeModel<M> | DeployedInstanceModel<M>,
|
|
26
|
+
public readonly target: DeploymentNodeModel<M> | DeployedInstanceModel<M>,
|
|
27
|
+
public readonly relations: RelationshipsAccum<M>,
|
|
28
|
+
) {
|
|
29
|
+
this.id = stringHash(`deployment:${source.id}:${target.id}`) as M['EdgeId']
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
readonly id: M['EdgeId']
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Human readable expression of the connection
|
|
36
|
+
* Mostly used for testing and debugging
|
|
37
|
+
*/
|
|
38
|
+
get expression(): string {
|
|
39
|
+
return `${this.source.id} -> ${this.target.id}`
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private _boundary: DeploymentNodeModel<M> | null | undefined
|
|
43
|
+
/**
|
|
44
|
+
* Common ancestor of the source and target elements.
|
|
45
|
+
* Represents the boundary of the connection.
|
|
46
|
+
*/
|
|
47
|
+
get boundary(): DeploymentNodeModel<M> | null {
|
|
48
|
+
this._boundary ??= this.source.commonAncestor(this.target)
|
|
49
|
+
return this._boundary
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
nonEmpty(): boolean {
|
|
53
|
+
return this.relations.nonEmpty
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
[customInspectSymbol](
|
|
57
|
+
// @ts-ignore
|
|
58
|
+
depth,
|
|
59
|
+
// @ts-ignore
|
|
60
|
+
inspectOptions,
|
|
61
|
+
// @ts-ignore
|
|
62
|
+
inspect,
|
|
63
|
+
) {
|
|
64
|
+
const asString = this.toString()
|
|
65
|
+
|
|
66
|
+
// Trick so that node displays the name of the constructor
|
|
67
|
+
Object.defineProperty(asString, 'constructor', {
|
|
68
|
+
value: DeploymentConnectionModel,
|
|
69
|
+
enumerable: false,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
return asString
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
toString() {
|
|
76
|
+
const model = [...this.relations.model].map(c => ' ' + c.expression)
|
|
77
|
+
if (model.length) {
|
|
78
|
+
model.unshift(' model:')
|
|
79
|
+
} else {
|
|
80
|
+
model.unshift(' model: []')
|
|
81
|
+
}
|
|
82
|
+
const deployment = [...this.relations.deployment].map(c => ' ' + c.expression)
|
|
83
|
+
if (deployment.length) {
|
|
84
|
+
deployment.unshift(' deployment:')
|
|
85
|
+
} else {
|
|
86
|
+
deployment.unshift(' deployment: []')
|
|
87
|
+
}
|
|
88
|
+
return [
|
|
89
|
+
this.expression,
|
|
90
|
+
...model,
|
|
91
|
+
...deployment,
|
|
92
|
+
].join('\n')
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Check if connection contains deployment relation,
|
|
97
|
+
* that is directly connected to source or target.
|
|
98
|
+
*/
|
|
99
|
+
public hasDirectDeploymentRelation(): boolean {
|
|
100
|
+
for (const relation of this.relations.deployment) {
|
|
101
|
+
if (relation.source.id === this.source.id || relation.target.id === this.target.id) {
|
|
102
|
+
return true
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return false
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public *values(): IteratorLike<RelationshipModel<M> | DeploymentRelationModel<M>> {
|
|
109
|
+
yield* this.relations.model
|
|
110
|
+
yield* this.relations.deployment
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Merge with another connection, if it has the same source and target.
|
|
115
|
+
* Returns new connection with union of relationships.
|
|
116
|
+
*/
|
|
117
|
+
public mergeWith(other: DeploymentConnectionModel<M>) {
|
|
118
|
+
invariant(this.source.id === other.source.id, 'Cannot merge connections with different sources')
|
|
119
|
+
invariant(this.target.id === other.target.id, 'Cannot merge connections with different targets')
|
|
120
|
+
return new DeploymentConnectionModel(
|
|
121
|
+
this.source,
|
|
122
|
+
this.target,
|
|
123
|
+
this.relations.union(other.relations),
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
public difference(other: DeploymentConnectionModel<M>) {
|
|
128
|
+
return new DeploymentConnectionModel(
|
|
129
|
+
this.source,
|
|
130
|
+
this.target,
|
|
131
|
+
this.relations.difference(other.relations),
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public intersect(other: DeploymentConnectionModel<M>) {
|
|
136
|
+
return new DeploymentConnectionModel(
|
|
137
|
+
this.source,
|
|
138
|
+
this.target,
|
|
139
|
+
this.relations.intersect(other.relations),
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
public equals(other: Connection): boolean {
|
|
144
|
+
invariant(other instanceof DeploymentConnectionModel, 'Other should ne DeploymentConnectionModel')
|
|
145
|
+
return this.id === other.id
|
|
146
|
+
&& this.source.id === other.source.id
|
|
147
|
+
&& this.target.id === other.target.id
|
|
148
|
+
&& equals(this.relations.model, other.relations.model)
|
|
149
|
+
&& equals(this.relations.deployment, other.relations.deployment)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Creates a clone of the current `DeploymentConnectionModel` instance with optional overrides.
|
|
154
|
+
* if `null` is provided in overrides, the corresponding relation set will be empty.
|
|
155
|
+
*/
|
|
156
|
+
public update(overrides?: {
|
|
157
|
+
model?: ReadonlySet<RelationshipModel<M>> | null
|
|
158
|
+
deployment?: ReadonlySet<DeploymentRelationModel<M>> | null
|
|
159
|
+
}): DeploymentConnectionModel<M> {
|
|
160
|
+
if (overrides) {
|
|
161
|
+
overrides = {
|
|
162
|
+
model: this.relations.model,
|
|
163
|
+
deployment: this.relations.deployment,
|
|
164
|
+
...overrides,
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return new DeploymentConnectionModel(
|
|
168
|
+
this.source,
|
|
169
|
+
this.target,
|
|
170
|
+
overrides
|
|
171
|
+
? new RelationshipsAccum(
|
|
172
|
+
overrides.model ?? new Set(),
|
|
173
|
+
overrides.deployment ?? new Set(),
|
|
174
|
+
)
|
|
175
|
+
: this.relations,
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import { isSameHierarchy } from '
|
|
2
|
-
import {
|
|
3
|
-
import type { AnyAux } from '
|
|
1
|
+
import { isSameHierarchy } from '../../../utils'
|
|
2
|
+
import type { DeploymentElementModel } from '../../DeploymentElementModel'
|
|
3
|
+
import type { AnyAux } from '../../types'
|
|
4
4
|
import { DeploymentConnectionModel } from './DeploymentConnectionModel'
|
|
5
5
|
|
|
6
|
-
export { mergeConnections } from './model'
|
|
7
|
-
|
|
8
|
-
export { DeploymentConnectionModel }
|
|
9
|
-
|
|
10
6
|
/**
|
|
11
7
|
* Resolve connection from source to target
|
|
12
8
|
* If direction is `both`, also look for reverse connection
|
|
@@ -16,14 +12,14 @@ export { DeploymentConnectionModel }
|
|
|
16
12
|
export function findConnection<M extends AnyAux>(
|
|
17
13
|
source: DeploymentElementModel<M>,
|
|
18
14
|
target: DeploymentElementModel<NoInfer<M>>,
|
|
19
|
-
direction: 'directed'
|
|
15
|
+
direction: 'directed',
|
|
20
16
|
):
|
|
21
17
|
| readonly [DeploymentConnectionModel<M>]
|
|
22
18
|
| readonly []
|
|
23
19
|
export function findConnection<M extends AnyAux>(
|
|
24
20
|
source: DeploymentElementModel<M>,
|
|
25
21
|
target: DeploymentElementModel<NoInfer<M>>,
|
|
26
|
-
direction: 'both'
|
|
22
|
+
direction: 'both',
|
|
27
23
|
):
|
|
28
24
|
| readonly [DeploymentConnectionModel<M>, DeploymentConnectionModel<M>]
|
|
29
25
|
| readonly [DeploymentConnectionModel<M>]
|
|
@@ -31,7 +27,7 @@ export function findConnection<M extends AnyAux>(
|
|
|
31
27
|
export function findConnection<M extends AnyAux>(
|
|
32
28
|
source: DeploymentElementModel<M>,
|
|
33
29
|
target: DeploymentElementModel<NoInfer<M>>,
|
|
34
|
-
direction?: 'directed' | 'both'
|
|
30
|
+
direction?: 'directed' | 'both',
|
|
35
31
|
):
|
|
36
32
|
| readonly [DeploymentConnectionModel<M>, DeploymentConnectionModel<M>]
|
|
37
33
|
| readonly [DeploymentConnectionModel<M>]
|
|
@@ -39,7 +35,7 @@ export function findConnection<M extends AnyAux>(
|
|
|
39
35
|
export function findConnection<M extends AnyAux>(
|
|
40
36
|
source: DeploymentElementModel<M>,
|
|
41
37
|
target: DeploymentElementModel<NoInfer<M>>,
|
|
42
|
-
direction: 'directed' | 'both' = 'directed'
|
|
38
|
+
direction: 'directed' | 'both' = 'directed',
|
|
43
39
|
) {
|
|
44
40
|
if (source === target) {
|
|
45
41
|
return []
|
|
@@ -54,8 +50,8 @@ export function findConnection<M extends AnyAux>(
|
|
|
54
50
|
new DeploymentConnectionModel<M>(
|
|
55
51
|
source,
|
|
56
52
|
target,
|
|
57
|
-
directedIntersection
|
|
58
|
-
)
|
|
53
|
+
directedIntersection,
|
|
54
|
+
),
|
|
59
55
|
] as const
|
|
60
56
|
: [] as const
|
|
61
57
|
|
|
@@ -64,7 +60,7 @@ export function findConnection<M extends AnyAux>(
|
|
|
64
60
|
}
|
|
65
61
|
return [
|
|
66
62
|
...directed,
|
|
67
|
-
...findConnection(target, source, 'directed')
|
|
63
|
+
...findConnection(target, source, 'directed'),
|
|
68
64
|
] as const
|
|
69
65
|
}
|
|
70
66
|
|
|
@@ -77,7 +73,7 @@ export function findConnection<M extends AnyAux>(
|
|
|
77
73
|
export function findConnectionsBetween<M extends AnyAux>(
|
|
78
74
|
element: DeploymentElementModel<M>,
|
|
79
75
|
others: Iterable<DeploymentElementModel<NoInfer<M>>>,
|
|
80
|
-
direction: 'directed' | 'both' = 'both'
|
|
76
|
+
direction: 'directed' | 'both' = 'both',
|
|
81
77
|
): readonly DeploymentConnectionModel<M>[] {
|
|
82
78
|
if (element.allIncoming.isEmpty && element.allOutgoing.isEmpty) {
|
|
83
79
|
return []
|
|
@@ -101,7 +97,7 @@ export function findConnectionsBetween<M extends AnyAux>(
|
|
|
101
97
|
}
|
|
102
98
|
return [
|
|
103
99
|
...outgoing,
|
|
104
|
-
...incoming
|
|
100
|
+
...incoming,
|
|
105
101
|
]
|
|
106
102
|
}
|
|
107
103
|
|
|
@@ -109,7 +105,7 @@ export function findConnectionsBetween<M extends AnyAux>(
|
|
|
109
105
|
* Resolve all connections within a given set of elements
|
|
110
106
|
*/
|
|
111
107
|
export function findConnectionsWithin<M extends AnyAux>(
|
|
112
|
-
elements: Iterable<DeploymentElementModel<M
|
|
108
|
+
elements: Iterable<DeploymentElementModel<M>>,
|
|
113
109
|
): readonly DeploymentConnectionModel<M>[] {
|
|
114
110
|
return [...elements].reduce((acc, el, index, array) => {
|
|
115
111
|
// skip for last element
|
|
@@ -117,7 +113,7 @@ export function findConnectionsWithin<M extends AnyAux>(
|
|
|
117
113
|
return acc
|
|
118
114
|
}
|
|
119
115
|
acc.push(
|
|
120
|
-
...findConnectionsBetween(el, array.slice(index + 1), 'both')
|
|
116
|
+
...findConnectionsBetween(el, array.slice(index + 1), 'both'),
|
|
121
117
|
)
|
|
122
118
|
return acc
|
|
123
119
|
}, [] as DeploymentConnectionModel<M>[])
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export { Connection } from './Connection'
|
|
2
|
+
export { DeploymentConnectionModel } from './deployment/DeploymentConnectionModel'
|
|
3
|
+
export { ConnectionModel } from './model/ConnectionModel'
|
|
4
|
+
export {
|
|
5
|
+
differenceConnections,
|
|
6
|
+
findAscendingConnections,
|
|
7
|
+
findDeepestNestedConnection,
|
|
8
|
+
findDescendantConnections,
|
|
9
|
+
hasSameSource,
|
|
10
|
+
hasSameSourceTarget,
|
|
11
|
+
hasSameTarget,
|
|
12
|
+
isAnyInOut,
|
|
13
|
+
isIncoming,
|
|
14
|
+
isNestedConnection,
|
|
15
|
+
isOutgoing,
|
|
16
|
+
mergeConnections,
|
|
17
|
+
sortConnectionsByBoundaryHierarchy,
|
|
18
|
+
sortDeepestFirst,
|
|
19
|
+
} from './ops'
|
|
20
|
+
|
|
21
|
+
export * as deployment from './deployment/find'
|
|
22
|
+
export * as model from './model/find'
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { isNot } from 'remeda'
|
|
2
|
+
import { invariant } from '../../../errors'
|
|
3
|
+
import { customInspectSymbol } from '../../../utils/const'
|
|
4
|
+
import { ifilter, isome } from '../../../utils/iterable'
|
|
5
|
+
import { difference, equals, intersection, union } from '../../../utils/set'
|
|
6
|
+
import { stringHash } from '../../../utils/string-hash'
|
|
7
|
+
import type { ElementModel } from '../../ElementModel'
|
|
8
|
+
import type { RelationshipModel } from '../../RelationModel'
|
|
9
|
+
import type { AnyAux } from '../../types'
|
|
10
|
+
import type { Connection } from '../Connection'
|
|
11
|
+
import { hasSameSourceTarget } from '../ops'
|
|
12
|
+
import { findConnection } from './find'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Connection refers to any relationships between two elements,
|
|
16
|
+
* both direct and implicit ones (between their nested elements).
|
|
17
|
+
*
|
|
18
|
+
* Merges relationships together to an single edge on the diagram.
|
|
19
|
+
*/
|
|
20
|
+
export class ConnectionModel<M extends AnyAux = AnyAux> implements Connection<ElementModel<M>, M['EdgeId']> {
|
|
21
|
+
public readonly id: M['EdgeId']
|
|
22
|
+
|
|
23
|
+
constructor(
|
|
24
|
+
public readonly source: ElementModel<M>,
|
|
25
|
+
public readonly target: ElementModel<M>,
|
|
26
|
+
public readonly relations: ReadonlySet<RelationshipModel<M>> = new Set(),
|
|
27
|
+
) {
|
|
28
|
+
this.id = stringHash(`model:${source.id}:${target.id}`) as M['EdgeId']
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private _boundary: ElementModel<M> | null | undefined
|
|
32
|
+
/**
|
|
33
|
+
* Common ancestor of the source and target elements.
|
|
34
|
+
* Represents the boundary of the connection.
|
|
35
|
+
*/
|
|
36
|
+
get boundary(): ElementModel<M> | null {
|
|
37
|
+
return this._boundary ??= this.source.commonAncestor(this.target)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Human readable expression of the connection
|
|
42
|
+
* Mostly used for testing and debugging
|
|
43
|
+
*/
|
|
44
|
+
get expression(): string {
|
|
45
|
+
return `${this.source.id} -> ${this.target.id}`
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Returns true if only includes relations between the source and target elements.
|
|
50
|
+
*/
|
|
51
|
+
get isDirect(): boolean {
|
|
52
|
+
return this.nonEmpty() && !this.isImplicit
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Returns true if includes relations between nested elements of the source and target elements.
|
|
57
|
+
*/
|
|
58
|
+
get isImplicit(): boolean {
|
|
59
|
+
return this.nonEmpty() && isome(this.relations, isNot(hasSameSourceTarget(this)))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get directRelations(): ReadonlySet<RelationshipModel<M>> {
|
|
63
|
+
return new Set(ifilter(this.relations, hasSameSourceTarget(this)))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
nonEmpty(): boolean {
|
|
67
|
+
return this.relations.size > 0
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
mergeWith(other: ConnectionModel<M>) {
|
|
71
|
+
invariant(this.source.id === other.source.id, 'Cannot merge connections with different sources')
|
|
72
|
+
invariant(this.target.id === other.target.id, 'Cannot merge connections with different targets')
|
|
73
|
+
return new ConnectionModel(
|
|
74
|
+
this.source,
|
|
75
|
+
this.target,
|
|
76
|
+
union(this.relations, other.relations),
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
difference(other: ConnectionModel<M>) {
|
|
81
|
+
return new ConnectionModel(
|
|
82
|
+
this.source,
|
|
83
|
+
this.target,
|
|
84
|
+
difference(this.relations, other.relations),
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
intersect(other: ConnectionModel<M>) {
|
|
89
|
+
invariant(other instanceof ConnectionModel, 'Cannot intersect connection with different type')
|
|
90
|
+
return new ConnectionModel(
|
|
91
|
+
this.source,
|
|
92
|
+
this.target,
|
|
93
|
+
intersection(this.relations, other.relations),
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
equals(other: Connection): boolean {
|
|
98
|
+
invariant(other instanceof ConnectionModel, 'Cannot merge connection with different type')
|
|
99
|
+
return this.id === other.id
|
|
100
|
+
&& this.source.id === other.source.id
|
|
101
|
+
&& this.target.id === other.target.id
|
|
102
|
+
&& equals(this.relations, other.relations)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Returns a new instance with the updated relations.
|
|
107
|
+
*
|
|
108
|
+
* @param relations - A readonly set of `RelationshipModel` instances representing the new relations.
|
|
109
|
+
* @returns A new `ConnectionModel` instance with the updated relations.
|
|
110
|
+
*/
|
|
111
|
+
update(relations: ReadonlySet<RelationshipModel<M>>): ConnectionModel<M> {
|
|
112
|
+
return new ConnectionModel(
|
|
113
|
+
this.source,
|
|
114
|
+
this.target,
|
|
115
|
+
relations,
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
[customInspectSymbol](
|
|
120
|
+
// @ts-ignore
|
|
121
|
+
depth,
|
|
122
|
+
// @ts-ignore
|
|
123
|
+
inspectOptions,
|
|
124
|
+
// @ts-ignore
|
|
125
|
+
inspect,
|
|
126
|
+
) {
|
|
127
|
+
const asString = this.toString()
|
|
128
|
+
|
|
129
|
+
// Trick so that node displays the name of the constructor
|
|
130
|
+
Object.defineProperty(asString, 'constructor', {
|
|
131
|
+
value: ConnectionModel,
|
|
132
|
+
enumerable: false,
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
return asString
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
toString() {
|
|
139
|
+
return [
|
|
140
|
+
this.expression,
|
|
141
|
+
this.relations.size ? ' relations:' : ' relations: [ ]',
|
|
142
|
+
...[...this.relations].map(c => ' ' + c.expression),
|
|
143
|
+
].join('\n')
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Creates a new connection with reversed direction (target becomes source and vice versa)
|
|
148
|
+
* @param search - When true, attempts to find an existing connection between the reversed nodes
|
|
149
|
+
*/
|
|
150
|
+
reversed(search = false): ConnectionModel<M> {
|
|
151
|
+
if (!search) {
|
|
152
|
+
return new ConnectionModel(this.target, this.source)
|
|
153
|
+
}
|
|
154
|
+
const [found] = findConnection(this.target, this.source, 'directed')
|
|
155
|
+
return found ?? new ConnectionModel(this.target, this.source, new Set())
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -1,36 +1,23 @@
|
|
|
1
|
-
import { isSameHierarchy } from '
|
|
2
|
-
import { intersection } from '
|
|
3
|
-
import type { ElementModel } from '
|
|
4
|
-
import type { AnyAux } from '
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
export function mergeConnections<C extends Connection<any, any>>(
|
|
8
|
-
connections: readonly C[]
|
|
9
|
-
): C[] {
|
|
10
|
-
const map = new Map<C['id'], C>()
|
|
11
|
-
for (const conn of connections) {
|
|
12
|
-
const existing = map.get(conn.id)
|
|
13
|
-
if (existing) {
|
|
14
|
-
map.set(conn.id, existing.mergeWith(conn) as any)
|
|
15
|
-
} else {
|
|
16
|
-
map.set(conn.id, conn)
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return [...map.values()]
|
|
20
|
-
}
|
|
1
|
+
import { isSameHierarchy } from '../../../utils/fqn'
|
|
2
|
+
import { intersection } from '../../../utils/set'
|
|
3
|
+
import type { ElementModel } from '../../ElementModel'
|
|
4
|
+
import type { AnyAux } from '../../types'
|
|
5
|
+
import { ConnectionModel } from './ConnectionModel'
|
|
21
6
|
|
|
22
7
|
/**
|
|
23
8
|
* Resolve connection from source to target
|
|
24
|
-
*
|
|
9
|
+
*
|
|
10
|
+
* @param direction - if 'both', also returns connection from target to source
|
|
11
|
+
* @default `directed`
|
|
25
12
|
*/
|
|
26
13
|
export function findConnection<M extends AnyAux>(
|
|
27
14
|
source: ElementModel<M>,
|
|
28
|
-
target: ElementModel<
|
|
29
|
-
direction: 'directed' | 'both' = 'directed'
|
|
15
|
+
target: ElementModel<M>,
|
|
16
|
+
direction: 'directed' | 'both' = 'directed',
|
|
30
17
|
):
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
18
|
+
| [ConnectionModel<M>, ConnectionModel<M>]
|
|
19
|
+
| [ConnectionModel<M>]
|
|
20
|
+
| []
|
|
34
21
|
{
|
|
35
22
|
if (source === target) {
|
|
36
23
|
return []
|
|
@@ -42,41 +29,48 @@ export function findConnection<M extends AnyAux>(
|
|
|
42
29
|
const directedIntersection = intersection(source.allOutgoing, target.allIncoming)
|
|
43
30
|
|
|
44
31
|
const directed = directedIntersection.size > 0
|
|
45
|
-
?
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
] as const
|
|
52
|
-
: [] as const
|
|
32
|
+
? new ConnectionModel(
|
|
33
|
+
source,
|
|
34
|
+
target,
|
|
35
|
+
directedIntersection,
|
|
36
|
+
)
|
|
37
|
+
: null
|
|
53
38
|
|
|
54
39
|
if (direction === 'directed') {
|
|
55
|
-
return directed
|
|
40
|
+
return directed ? [directed] : []
|
|
56
41
|
}
|
|
57
42
|
|
|
58
43
|
const reverseIntersection = intersection(source.allIncoming, target.allOutgoing)
|
|
59
44
|
const reverse = reverseIntersection.size > 0
|
|
60
|
-
?
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
45
|
+
? new ConnectionModel(
|
|
46
|
+
target,
|
|
47
|
+
source,
|
|
48
|
+
reverseIntersection,
|
|
49
|
+
)
|
|
50
|
+
: null
|
|
51
|
+
|
|
52
|
+
if (directed && reverse) {
|
|
53
|
+
return [directed, reverse]
|
|
54
|
+
}
|
|
55
|
+
if (directed) {
|
|
56
|
+
return [directed]
|
|
57
|
+
}
|
|
58
|
+
if (reverse) {
|
|
59
|
+
return [reverse]
|
|
60
|
+
}
|
|
68
61
|
|
|
69
|
-
return [
|
|
62
|
+
return []
|
|
70
63
|
}
|
|
71
64
|
|
|
72
65
|
/**
|
|
73
66
|
* Resolve all connections between element and others
|
|
74
|
-
*
|
|
67
|
+
* @param direction - if 'directed', only look for outgoing connections from the element to others
|
|
68
|
+
* @default `both`
|
|
75
69
|
*/
|
|
76
70
|
export function findConnectionsBetween<M extends AnyAux>(
|
|
77
71
|
element: ElementModel<M>,
|
|
78
72
|
others: Iterable<ElementModel<NoInfer<M>>>,
|
|
79
|
-
direction: 'directed' | 'both' = 'both'
|
|
73
|
+
direction: 'directed' | 'both' = 'both',
|
|
80
74
|
): readonly ConnectionModel<M>[] {
|
|
81
75
|
if (element.allIncoming.size === 0 && element.allOutgoing.size === 0) {
|
|
82
76
|
return []
|
|
@@ -100,7 +94,7 @@ export function findConnectionsBetween<M extends AnyAux>(
|
|
|
100
94
|
}
|
|
101
95
|
return [
|
|
102
96
|
...outgoing,
|
|
103
|
-
...incoming
|
|
97
|
+
...incoming,
|
|
104
98
|
]
|
|
105
99
|
}
|
|
106
100
|
|
|
@@ -108,7 +102,7 @@ export function findConnectionsBetween<M extends AnyAux>(
|
|
|
108
102
|
* Resolve all connections within a given set of elements
|
|
109
103
|
*/
|
|
110
104
|
export function findConnectionsWithin<M extends AnyAux>(
|
|
111
|
-
elements: Iterable<ElementModel<M
|
|
105
|
+
elements: Iterable<ElementModel<M>>,
|
|
112
106
|
): readonly ConnectionModel<M>[] {
|
|
113
107
|
return [...elements].reduce((acc, el, index, array) => {
|
|
114
108
|
// skip for last element
|
|
@@ -116,7 +110,7 @@ export function findConnectionsWithin<M extends AnyAux>(
|
|
|
116
110
|
return acc
|
|
117
111
|
}
|
|
118
112
|
acc.push(
|
|
119
|
-
...findConnectionsBetween(el, array.slice(index + 1), 'both')
|
|
113
|
+
...findConnectionsBetween(el, array.slice(index + 1), 'both'),
|
|
120
114
|
)
|
|
121
115
|
return acc
|
|
122
116
|
}, [] as ConnectionModel<M>[])
|