@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,147 @@
|
|
|
1
|
+
import type { Connection } from '../../model/connection'
|
|
2
|
+
import type { DeploymentElementModel } from '../../model/DeploymentElementModel'
|
|
3
|
+
import type { ElementModel } from '../../model/ElementModel'
|
|
4
|
+
|
|
5
|
+
type Element = ElementModel | DeploymentElementModel
|
|
6
|
+
|
|
7
|
+
export interface ComputeCtx<
|
|
8
|
+
E extends Element,
|
|
9
|
+
C extends Connection<E, any>,
|
|
10
|
+
M extends ComputeMemory<any>,
|
|
11
|
+
Inc extends StageInclude<any>,
|
|
12
|
+
Exc extends StageExclude<any>,
|
|
13
|
+
Expr = unknown,
|
|
14
|
+
> {
|
|
15
|
+
Element: E
|
|
16
|
+
Connection: C
|
|
17
|
+
Memory: M
|
|
18
|
+
StageInclude: Inc
|
|
19
|
+
StageExclude: Exc
|
|
20
|
+
Expr: Expr
|
|
21
|
+
MutableState: State<this>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// export type AnyCtx = ComputeCtx<any, any, ComputeMemory<any>, StageInclude<any>, StageExclude<any>, any>
|
|
25
|
+
export type AnyCtx = GenericCtx
|
|
26
|
+
|
|
27
|
+
export type GenericCtx = ComputeCtx<
|
|
28
|
+
any,
|
|
29
|
+
Connection<any, any>,
|
|
30
|
+
ComputeMemory<GenericCtx>,
|
|
31
|
+
StageInclude<GenericCtx>,
|
|
32
|
+
StageExclude<GenericCtx>,
|
|
33
|
+
unknown
|
|
34
|
+
>
|
|
35
|
+
|
|
36
|
+
export interface State<T extends AnyCtx> {
|
|
37
|
+
elements: Set<CtxElement<T>>
|
|
38
|
+
explicits: Set<CtxElement<T>>
|
|
39
|
+
final: Set<CtxElement<T>>
|
|
40
|
+
connections: Array<CtxConnection<T>>
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type MutableState<T extends AnyCtx> = T['MutableState']
|
|
44
|
+
|
|
45
|
+
export type StageExpression<T extends AnyCtx> = T['Expr']
|
|
46
|
+
|
|
47
|
+
export type CtxElement<T extends AnyCtx> = T['Element']
|
|
48
|
+
export type CtxConnection<T extends AnyCtx> = T['Connection']
|
|
49
|
+
export type CtxMemory<T extends AnyCtx> = T['Memory']
|
|
50
|
+
|
|
51
|
+
export interface ComputeMemory<T extends AnyCtx> {
|
|
52
|
+
/**
|
|
53
|
+
* All resolved elements, includes:
|
|
54
|
+
* - explicit elements (included by element predicates, always appear in the view unless excluded)
|
|
55
|
+
* - elements from resolved connections (may be excluded, if connection is redundant @see excludeRedundantRelationships
|
|
56
|
+
* - implicit elements (not added directly, not included in the view, used for resolving connections)
|
|
57
|
+
*/
|
|
58
|
+
readonly elements: ReadonlySet<CtxElement<T>>
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Explicit elements
|
|
62
|
+
*/
|
|
63
|
+
readonly explicits: ReadonlySet<CtxElement<T>>
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Final set of elements to be included in the view
|
|
67
|
+
* (`elements` excluding implicits)
|
|
68
|
+
* Keeps order in which elements were added
|
|
69
|
+
*/
|
|
70
|
+
readonly final: ReadonlySet<CtxElement<T>>
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Resolved connections
|
|
74
|
+
*/
|
|
75
|
+
readonly connections: ReadonlyArray<CtxConnection<T>>
|
|
76
|
+
|
|
77
|
+
isEmpty(): boolean
|
|
78
|
+
|
|
79
|
+
stageInclude(expression: T['Expr']): T['StageInclude']
|
|
80
|
+
|
|
81
|
+
stageExclude(expression: T['Expr']): T['StageExclude']
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Returns shallow copy of state
|
|
85
|
+
*/
|
|
86
|
+
mutableState(): T['MutableState']
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Returns new memory with updated state
|
|
90
|
+
*/
|
|
91
|
+
update(newstate: Partial<T['MutableState']>): T['Memory']
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
interface Stage<T extends AnyCtx> {
|
|
95
|
+
readonly memory: CtxMemory<T>
|
|
96
|
+
|
|
97
|
+
readonly expression: StageExpression<T>
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Has changes
|
|
101
|
+
*/
|
|
102
|
+
isDirty(): boolean
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Has no changes
|
|
106
|
+
*/
|
|
107
|
+
isEmpty(): boolean
|
|
108
|
+
|
|
109
|
+
commit(): T['Memory']
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface StageInclude<T extends AnyCtx> extends Stage<T> {
|
|
113
|
+
readonly elements: ReadonlySet<CtxElement<T>>
|
|
114
|
+
|
|
115
|
+
readonly connections: ReadonlyArray<CtxConnection<T>>
|
|
116
|
+
|
|
117
|
+
addExplicit(element: CtxElement<T> | Iterable<CtxElement<T>> | false | undefined | null): this
|
|
118
|
+
|
|
119
|
+
addImplicit(element: CtxElement<T> | Iterable<CtxElement<T>> | false | undefined | null): this
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Connects element with existing ones in the memory
|
|
123
|
+
*
|
|
124
|
+
* @param direction - direction of the connection
|
|
125
|
+
* @default 'both'
|
|
126
|
+
*/
|
|
127
|
+
connectWithExisting(
|
|
128
|
+
element: CtxElement<T> | Iterable<CtxElement<T>>,
|
|
129
|
+
direction?: 'in' | 'out' | 'both',
|
|
130
|
+
): boolean
|
|
131
|
+
|
|
132
|
+
addConnections(connection: CtxConnection<T> | Iterable<CtxConnection<T>>): this
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface StageExclude<T extends AnyCtx> extends Stage<T> {
|
|
136
|
+
/**
|
|
137
|
+
* Excludes from the memory relationships from given connections (still connection may be included, but without given relationships)
|
|
138
|
+
* @param moveExplicitToImplicit - if true, disconnected explicit elements will be moved to implicit
|
|
139
|
+
* @default false
|
|
140
|
+
*/
|
|
141
|
+
excludeConnections(
|
|
142
|
+
connection: CtxConnection<T> | Iterable<CtxConnection<T>>,
|
|
143
|
+
moveExplicitToImplicit?: boolean,
|
|
144
|
+
): this
|
|
145
|
+
|
|
146
|
+
exclude(element: CtxElement<T> | Iterable<CtxElement<T>> | false | undefined | null): this
|
|
147
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import DefaultMap from 'mnemonist/default-map'
|
|
2
|
+
import { Connection } from '../../model'
|
|
3
|
+
import { isAncestor, sortParentsFirst } from '../../utils'
|
|
4
|
+
import { toArray } from '../../utils/iterable'
|
|
5
|
+
import type { AnyCtx, ComputeMemory, CtxElement } from './_types'
|
|
6
|
+
|
|
7
|
+
export function treeFromMemoryState<T extends AnyCtx, M extends ComputeMemory<T> = ComputeMemory<T>>(
|
|
8
|
+
memory: M,
|
|
9
|
+
filter: 'all' | 'final' = 'final',
|
|
10
|
+
) {
|
|
11
|
+
const sorted = sortParentsFirst(toArray(filter === 'final' ? memory.final : memory.elements))
|
|
12
|
+
const connected = new Set(memory.connections.flatMap(c => [c.source, c.target]))
|
|
13
|
+
const root = new Set(sorted)
|
|
14
|
+
const parents = new DefaultMap<CtxElement<T>, CtxElement<T> | null>(() => null)
|
|
15
|
+
const children = sorted.reduce((acc, parent, index, all) => {
|
|
16
|
+
acc.set(
|
|
17
|
+
parent,
|
|
18
|
+
all
|
|
19
|
+
.slice(index + 1)
|
|
20
|
+
.filter(e => isAncestor(parent, e))
|
|
21
|
+
.map(e => {
|
|
22
|
+
root.delete(e)
|
|
23
|
+
return e
|
|
24
|
+
})
|
|
25
|
+
.reduce((acc, el) => {
|
|
26
|
+
if (!acc.some(e => isAncestor(e, el))) {
|
|
27
|
+
acc.push(el)
|
|
28
|
+
parents.set(el, parent)
|
|
29
|
+
}
|
|
30
|
+
return acc
|
|
31
|
+
}, [] as CtxElement<T>[]),
|
|
32
|
+
)
|
|
33
|
+
return acc
|
|
34
|
+
}, new DefaultMap<CtxElement<T>, ReadonlyArray<CtxElement<T>>>(() => []))
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
root: root as ReadonlySet<CtxElement<T>>,
|
|
38
|
+
connected: connected as ReadonlySet<CtxElement<T>>,
|
|
39
|
+
hasInOut: (el: CtxElement<T>) => memory.connections.some(Connection.isAnyInOut(el.id)),
|
|
40
|
+
parents: (el: CtxElement<T>) => children.get(el),
|
|
41
|
+
children: (el: CtxElement<T>) => children.get(el),
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
type NodeId
|
|
11
11
|
} from '../../types'
|
|
12
12
|
import { compareByFqnHierarchically, parentFqn } from '../../utils/fqn'
|
|
13
|
-
import { NodesGroup } from '../element-view/
|
|
13
|
+
import { NodesGroup } from '../element-view/memory'
|
|
14
14
|
|
|
15
15
|
function updateDepthOfAncestors(node: ComputedNode, nodes: ReadonlyMap<Fqn, ComputedNode>) {
|
|
16
16
|
let parentNd
|
|
@@ -44,13 +44,13 @@ export function buildComputedNodesFromElements(elements: ReadonlyArray<Element>,
|
|
|
44
44
|
|
|
45
45
|
export function buildComputedNodes(
|
|
46
46
|
elements: ReadonlyArray<ComputedNodeSource>,
|
|
47
|
-
groups?: NodesGroup
|
|
47
|
+
groups?: ReadonlyArray<NodesGroup>
|
|
48
48
|
): ReadonlyMap<Fqn, ComputedNode> {
|
|
49
49
|
const nodesMap = new Map<Fqn, ComputedNode>()
|
|
50
50
|
|
|
51
51
|
const elementToGroup = new Map<Fqn, NodeId>()
|
|
52
52
|
|
|
53
|
-
groups?.forEach(({ id, parent, viewRule,
|
|
53
|
+
groups?.forEach(({ id, parent, viewRule, elements }) => {
|
|
54
54
|
if (parent) {
|
|
55
55
|
nonNullable(nodesMap.get(parent), `Parent group node ${parent} not found`).children.push(id)
|
|
56
56
|
}
|
|
@@ -75,7 +75,7 @@ export function buildComputedNodes(
|
|
|
75
75
|
opacity: viewRule.opacity ?? 0
|
|
76
76
|
}
|
|
77
77
|
})
|
|
78
|
-
for (const e of
|
|
78
|
+
for (const e of elements) {
|
|
79
79
|
elementToGroup.set(e.id, id)
|
|
80
80
|
}
|
|
81
81
|
})
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { isNullish } from 'remeda'
|
|
2
2
|
import { nonexhaustive } from '../../errors'
|
|
3
3
|
import {
|
|
4
|
-
type ComputedNode,
|
|
5
|
-
type Element,
|
|
6
4
|
type ElementPredicateExpression,
|
|
7
5
|
isCustomElement,
|
|
8
6
|
isElementKindExpr,
|
|
@@ -17,7 +15,7 @@ import { parentFqn } from '../../utils'
|
|
|
17
15
|
|
|
18
16
|
type Predicate<T> = (x: T) => boolean
|
|
19
17
|
|
|
20
|
-
export function elementExprToPredicate<T extends
|
|
18
|
+
export function elementExprToPredicate<T extends { id: string; tags?: readonly string[] | null; kind: string }>(
|
|
21
19
|
target: ElementPredicateExpression
|
|
22
20
|
): Predicate<T> {
|
|
23
21
|
if (isElementWhere(target)) {
|
|
@@ -19,12 +19,8 @@ export function linkNodesWithEdges(nodesMap: ReadonlyMap<Fqn, ComputedNode>, edg
|
|
|
19
19
|
source.outEdges.push(edge.id)
|
|
20
20
|
target.inEdges.push(edge.id)
|
|
21
21
|
|
|
22
|
-
if (edge.dir === 'both') {
|
|
23
|
-
source.inEdges.push(edge.id)
|
|
24
|
-
target.outEdges.push(edge.id)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
22
|
// These ancestors are reversed: from bottom to top
|
|
23
|
+
// Ensure that ancestors are in computed nodes
|
|
28
24
|
const sourceAncestors = ancestorsOfNode(source, nodesMap)
|
|
29
25
|
const targetAncestors = ancestorsOfNode(target, nodesMap)
|
|
30
26
|
|
|
@@ -34,8 +30,8 @@ export function linkNodesWithEdges(nodesMap: ReadonlyMap<Fqn, ComputedNode>, edg
|
|
|
34
30
|
? last(
|
|
35
31
|
commonHead(
|
|
36
32
|
reverse(sourceAncestors),
|
|
37
|
-
reverse(targetAncestors)
|
|
38
|
-
)
|
|
33
|
+
reverse(targetAncestors),
|
|
34
|
+
),
|
|
39
35
|
)
|
|
40
36
|
: null
|
|
41
37
|
edge.parent = edgeParent ? edgeParent.id : null
|
|
@@ -1,27 +1,45 @@
|
|
|
1
|
-
import { isTruthy, only,
|
|
1
|
+
import { isTruthy, only, pickBy, pipe, reduce, unique } from 'remeda'
|
|
2
2
|
import type { Color, DeploymentRelation, Link, Tag, ViewId } from '../../types'
|
|
3
|
-
import type {
|
|
3
|
+
import type { ModelRelation, RelationshipArrowType, RelationshipKind, RelationshipLineType } from '../../types/relation'
|
|
4
4
|
import { isNonEmptyArray } from '../../utils'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
6
|
+
function pickRelationshipProps(relation: ModelRelation | DeploymentRelation) {
|
|
7
|
+
const {
|
|
8
|
+
title,
|
|
9
|
+
description = null,
|
|
10
|
+
technology = null,
|
|
11
|
+
kind = null,
|
|
12
|
+
color = null,
|
|
13
|
+
line = null,
|
|
14
|
+
head = null,
|
|
15
|
+
tail = null,
|
|
16
|
+
navigateTo = null
|
|
17
|
+
} = relation
|
|
18
|
+
return {
|
|
19
|
+
// Pick description only if title is present
|
|
20
|
+
...(title && {
|
|
21
|
+
title,
|
|
22
|
+
description
|
|
23
|
+
}),
|
|
24
|
+
technology,
|
|
25
|
+
kind,
|
|
26
|
+
color,
|
|
27
|
+
line,
|
|
28
|
+
head,
|
|
29
|
+
tail,
|
|
30
|
+
navigateTo
|
|
31
|
+
}
|
|
20
32
|
}
|
|
21
|
-
type RelationshipProps = ReturnType<typeof pickRelationshipProps>
|
|
22
33
|
|
|
23
|
-
|
|
24
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Merges properties from multiple relationships into a single object.
|
|
36
|
+
* @param relations - The relationships to merge.
|
|
37
|
+
* @param prefer - The relationship to prefer when merging.
|
|
38
|
+
*/
|
|
39
|
+
export function mergePropsFromRelationships(
|
|
40
|
+
relations: Array<ModelRelation | DeploymentRelation>,
|
|
41
|
+
prefer?: ModelRelation | DeploymentRelation
|
|
42
|
+
) {
|
|
25
43
|
const allprops = pipe(
|
|
26
44
|
relations,
|
|
27
45
|
reduce(
|
|
@@ -76,12 +94,17 @@ export function deriveEdgePropsFromRelationships(relations: Array<Relation | Dep
|
|
|
76
94
|
}
|
|
77
95
|
)
|
|
78
96
|
)
|
|
97
|
+
|
|
98
|
+
let technology = only(allprops.technology)
|
|
99
|
+
let title = only(allprops.title) ?? (allprops.title.length > 1 ? '[...]' : null)
|
|
100
|
+
|
|
79
101
|
const tags = unique(allprops.tags)
|
|
80
|
-
|
|
102
|
+
let merged = pickBy(
|
|
81
103
|
{
|
|
82
|
-
|
|
104
|
+
// If there is no title, but there is technology, use technology as title
|
|
105
|
+
title: title ?? (technology ? `[${technology}]` : null),
|
|
83
106
|
description: only(allprops.description),
|
|
84
|
-
technology
|
|
107
|
+
technology,
|
|
85
108
|
kind: only(allprops.kind),
|
|
86
109
|
head: only(allprops.head),
|
|
87
110
|
tail: only(allprops.tail),
|
|
@@ -93,5 +116,13 @@ export function deriveEdgePropsFromRelationships(relations: Array<Relation | Dep
|
|
|
93
116
|
},
|
|
94
117
|
isTruthy
|
|
95
118
|
)
|
|
119
|
+
|
|
120
|
+
if (prefer) {
|
|
121
|
+
return {
|
|
122
|
+
...merged,
|
|
123
|
+
...pickBy(pickRelationshipProps(prefer), isTruthy)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return merged
|
|
96
127
|
}
|
|
97
|
-
export type MergedRelationshipProps = ReturnType<typeof
|
|
128
|
+
export type MergedRelationshipProps = ReturnType<typeof mergePropsFromRelationships>
|
|
@@ -10,12 +10,12 @@ import {
|
|
|
10
10
|
type RelationWhereExpr
|
|
11
11
|
} from '../../types/expression'
|
|
12
12
|
import { whereOperatorAsPredicate } from '../../types/operators'
|
|
13
|
-
import type {
|
|
13
|
+
import type { ModelRelation } from '../../types/relation'
|
|
14
14
|
import type { ComputedNode } from '../../types/view'
|
|
15
15
|
import { elementExprToPredicate } from './elementExpressionToPredicate'
|
|
16
16
|
|
|
17
17
|
type Predicate<T> = (x: T) => boolean
|
|
18
|
-
export type FilterableEdge = Pick<
|
|
18
|
+
export type FilterableEdge = Pick<ModelRelation, 'kind' | 'tags'> & {
|
|
19
19
|
source: ComputedNode
|
|
20
20
|
target: ComputedNode
|
|
21
21
|
}
|
|
@@ -1,65 +1,69 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isExtendsElementView, type LikeC4View } from '../../types/view'
|
|
3
|
-
import { findCycles, Graph, isAcyclic, postorder } from '../../utils/graphlib'
|
|
1
|
+
import { values } from 'remeda'
|
|
2
|
+
import { type ExtendsElementView, isElementView, isExtendsElementView, type LikeC4View } from '../../types/view'
|
|
4
3
|
|
|
4
|
+
import Graph from 'graphology'
|
|
5
|
+
import { topologicalSort } from 'graphology-dag/topological-sort'
|
|
6
|
+
import willCreateCycle from 'graphology-dag/will-create-cycle'
|
|
5
7
|
/**
|
|
6
8
|
* Resolve rules of extended views
|
|
7
9
|
* (Removes invalid views)
|
|
8
10
|
*/
|
|
9
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
11
|
export function resolveRulesExtendedViews<V extends Record<any, LikeC4View>>(
|
|
11
12
|
unresolvedViews: V
|
|
12
13
|
): V {
|
|
13
|
-
const g = new Graph({
|
|
14
|
-
|
|
15
|
-
multigraph: false,
|
|
16
|
-
compound: false
|
|
14
|
+
const g = new Graph<{ view: LikeC4View }>({
|
|
15
|
+
type: 'directed'
|
|
17
16
|
})
|
|
17
|
+
const extendedViews = [] as ExtendsElementView<any, any>[]
|
|
18
18
|
for (const view of values(unresolvedViews)) {
|
|
19
|
-
g.
|
|
19
|
+
g.addNode(view.id, { view })
|
|
20
20
|
if (isExtendsElementView(view)) {
|
|
21
|
-
|
|
22
|
-
g.setEdge(view.id, view.extends)
|
|
21
|
+
extendedViews.push(view)
|
|
23
22
|
}
|
|
24
23
|
}
|
|
25
|
-
|
|
24
|
+
|
|
25
|
+
if (extendedViews.length === 0) {
|
|
26
26
|
return unresolvedViews
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
break
|
|
29
|
+
for (const view of extendedViews) {
|
|
30
|
+
if (!g.hasNode(view.extends)) {
|
|
31
|
+
console.warn(`View "${view.id}" extends from "${view.extends}" which does not exist`)
|
|
32
|
+
continue
|
|
34
33
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
if (willCreateCycle(g, view.id, view.extends)) {
|
|
35
|
+
console.warn(`View "${view.id}" extends from "${view.extends}" which creates a cycle`)
|
|
36
|
+
continue
|
|
38
37
|
}
|
|
39
|
-
|
|
38
|
+
|
|
39
|
+
// view -> parent
|
|
40
|
+
g.addDirectedEdge(view.id, view.extends)
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
const
|
|
43
|
+
const sorted = topologicalSort(g).reverse()
|
|
44
|
+
return sorted.reduce((acc, id) => {
|
|
45
|
+
const view = g.getNodeAttribute(id, 'view')
|
|
46
|
+
if (!isExtendsElementView(view)) {
|
|
47
|
+
acc[view.id] = view
|
|
48
|
+
return acc
|
|
49
|
+
}
|
|
43
50
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (!view) {
|
|
51
|
+
const extendsFrom = acc[view.extends]
|
|
52
|
+
if (!extendsFrom || !isElementView(extendsFrom)) {
|
|
47
53
|
return acc
|
|
48
54
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
return Object.assign(acc, {
|
|
56
|
-
[view.id]: {
|
|
57
|
-
...extendsFrom,
|
|
58
|
-
...view,
|
|
59
|
-
rules: [...extendsFrom.rules, ...view.rules]
|
|
60
|
-
}
|
|
61
|
-
})
|
|
55
|
+
|
|
56
|
+
acc[view.id] = {
|
|
57
|
+
...extendsFrom,
|
|
58
|
+
...view,
|
|
59
|
+
rules: [...extendsFrom.rules, ...view.rules]
|
|
62
60
|
}
|
|
63
|
-
return
|
|
64
|
-
}, {} as V
|
|
61
|
+
return acc
|
|
62
|
+
}, {} as Record<string, LikeC4View>) as V
|
|
63
|
+
|
|
64
|
+
// forEachNodeInTopologicalOrder(g, (_id, { view }) => {
|
|
65
|
+
|
|
66
|
+
// })
|
|
67
|
+
|
|
68
|
+
// return ordered as V
|
|
65
69
|
}
|
|
@@ -21,12 +21,12 @@ export function resolveGlobalRules<V extends DynamicView | ElementView>(
|
|
|
21
21
|
if (isElementView(view)) {
|
|
22
22
|
return {
|
|
23
23
|
...view,
|
|
24
|
-
rules: resolveGlobalRulesInElementView(view, globals)
|
|
24
|
+
rules: resolveGlobalRulesInElementView(view.rules, globals)
|
|
25
25
|
}
|
|
26
26
|
} else if (isDynamicView(view)) {
|
|
27
27
|
return {
|
|
28
28
|
...view,
|
|
29
|
-
rules: resolveGlobalRulesInDynamicView(view, globals)
|
|
29
|
+
rules: resolveGlobalRulesInDynamicView(view.rules, globals)
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
nonexhaustive(view)
|
|
@@ -35,10 +35,10 @@ export function resolveGlobalRules<V extends DynamicView | ElementView>(
|
|
|
35
35
|
type ViewRuleGlobal = ViewRuleGlobalPredicateRef | ViewRuleGlobalStyle
|
|
36
36
|
|
|
37
37
|
export function resolveGlobalRulesInElementView(
|
|
38
|
-
|
|
38
|
+
rules: ViewRule[],
|
|
39
39
|
globals: ModelGlobals
|
|
40
40
|
): Array<Exclude<ViewRule, ViewRuleGlobal>> {
|
|
41
|
-
return
|
|
41
|
+
return rules.reduce((acc, rule) => {
|
|
42
42
|
if (isViewRuleGlobalPredicateRef(rule)) {
|
|
43
43
|
const globalPredicates = globals.predicates[rule.predicateId]
|
|
44
44
|
if (isNullish(globalPredicates)) {
|
|
@@ -59,10 +59,10 @@ export function resolveGlobalRulesInElementView(
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
export function resolveGlobalRulesInDynamicView(
|
|
62
|
-
|
|
62
|
+
rules: DynamicViewRule[],
|
|
63
63
|
globals: ModelGlobals
|
|
64
64
|
): Array<Exclude<DynamicViewRule, ViewRuleGlobal>> {
|
|
65
|
-
return
|
|
65
|
+
return rules.reduce((acc, rule) => {
|
|
66
66
|
if (isViewRuleGlobalPredicateRef(rule)) {
|
|
67
67
|
const globalPredicates = globals.dynamicPredicates[rule.predicateId]
|
|
68
68
|
if (isNullish(globalPredicates)) {
|