@likec4/core 1.27.2 → 1.28.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/dist/builder/index.d.mts +6 -6
- package/dist/builder/index.mjs +23 -13
- package/dist/compute-view/index.d.mts +3 -3
- package/dist/compute-view/index.mjs +3 -3
- package/dist/compute-view/relationships-view/index.d.mts +3 -3
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +2 -2
- package/dist/model/index.d.mts +6 -6
- package/dist/model/index.mjs +1 -1
- package/dist/shared/{core.RjpTh3vE.mjs → core.Bjl6Kmc8.mjs} +260 -168
- package/dist/shared/{core.C9JdFlAd.d.mts → core.CZR9J1Qx.d.mts} +2 -2
- package/dist/shared/{core.C7IN6igU.d.mts → core.CiFKryGW.d.mts} +7 -2
- package/dist/shared/{core.HKn0jlo_.d.mts → core.D2830qgg.d.mts} +1 -1
- package/dist/shared/{core.z6g06scf.d.mts → core.DJvmfnEi.d.mts} +484 -189
- package/dist/shared/{core.BMep80lJ.mjs → core.DgfwjBtu.mjs} +198 -87
- package/dist/shared/core.SjLhMA7a.d.mts +62 -0
- package/dist/types/index.d.mts +4 -4
- package/dist/types/index.mjs +1 -1
- package/dist/utils/index.d.mts +2 -2
- package/package.json +6 -6
- package/src/builder/Builder.ts +1 -0
- package/src/builder/Builder.view-common.ts +10 -10
- package/src/builder/Builder.view-element.ts +26 -14
- package/src/builder/__snapshots__/Builder-style1.spec.ts.snap +23 -8
- package/src/builder/__snapshots__/Builder-style2.spec.ts.snap +9 -4
- package/src/compute-view/deployment-view/predicates/deploymentRefs-where.ts +18 -0
- package/src/compute-view/deployment-view/predicates/relation-direct.ts +6 -0
- package/src/compute-view/deployment-view/predicates/relation-in-out.ts +3 -0
- package/src/compute-view/deployment-view/predicates/relation-incoming.ts +3 -0
- package/src/compute-view/deployment-view/predicates/relation-outgoing.ts +3 -0
- package/src/compute-view/deployment-view/predicates/utils.ts +10 -3
- package/src/compute-view/deployment-view/utils.ts +7 -4
- package/src/compute-view/element-view/__test__/fixture.ts +72 -65
- package/src/compute-view/element-view/_types.ts +13 -9
- package/src/compute-view/element-view/compute.ts +27 -27
- package/src/compute-view/element-view/memory/NodeGroup.ts +4 -3
- package/src/compute-view/element-view/memory/memory.ts +3 -2
- package/src/compute-view/element-view/memory/stage-include.ts +2 -2
- package/src/compute-view/element-view/predicates/_utils.ts +25 -24
- package/src/compute-view/element-view/predicates/element-expand.ts +7 -10
- package/src/compute-view/element-view/predicates/element-kind-tag.ts +5 -3
- package/src/compute-view/element-view/predicates/element-ref.ts +7 -19
- package/src/compute-view/element-view/predicates/relation-direct.ts +15 -13
- package/src/compute-view/element-view/predicates/relation-in-out.ts +5 -6
- package/src/compute-view/element-view/predicates/relation-in.ts +36 -32
- package/src/compute-view/element-view/predicates/relation-out.ts +37 -33
- package/src/compute-view/element-view/predicates/wildcard.ts +2 -2
- package/src/compute-view/utils/applyCustomElementProperties.ts +9 -4
- package/src/compute-view/utils/applyCustomRelationProperties.ts +10 -7
- package/src/compute-view/utils/elementExpressionToPredicate.ts +27 -26
- package/src/compute-view/utils/link-nodes-with-edges.ts +1 -1
- package/src/compute-view/utils/relationExpressionToPredicates.ts +10 -17
- package/src/compute-view/utils/uniqueTags.ts +3 -3
- package/src/index.ts +0 -2
- package/src/model/ElementModel.ts +25 -6
- package/src/model/LikeC4Model.ts +44 -6
- package/src/model/RelationModel.ts +4 -2
- package/src/model/view/LikeC4ViewModel.ts +3 -2
- package/src/types/_common.ts +0 -4
- package/src/types/deployments.ts +3 -2
- package/src/types/element.ts +2 -10
- package/src/types/expression-v2-model.ts +314 -0
- package/src/types/expression-v2.ts +238 -30
- package/src/types/index.ts +15 -46
- package/src/types/model-data.ts +4 -1
- package/src/types/relation.ts +2 -1
- package/src/types/scalars.ts +39 -0
- package/src/types/view-changes.ts +2 -1
- package/src/types/view.ts +10 -11
- package/dist/shared/core.30yYKXcZ.d.mts +0 -186
- package/src/types/expression.ts +0 -207
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { anyPass, unique } from 'remeda'
|
|
2
2
|
import { nonexhaustive } from '../../../errors'
|
|
3
|
-
import type
|
|
4
|
-
import
|
|
3
|
+
import { type ConnectionModel, Connection, findConnectionsBetween } from '../../../model/connection/model'
|
|
4
|
+
import type { LikeC4Model } from '../../../model/LikeC4Model'
|
|
5
5
|
import type { RelationshipModel } from '../../../model/RelationModel'
|
|
6
6
|
import type { AnyAux } from '../../../model/types'
|
|
7
|
-
import
|
|
7
|
+
import { ModelLayer } from '../../../types/expression-v2-model'
|
|
8
8
|
import { toSet } from '../../../utils/iterable/to'
|
|
9
9
|
import { elementExprToPredicate } from '../../utils/elementExpressionToPredicate'
|
|
10
10
|
import type { ConnectionWhere, PredicateExecutor } from '../_types'
|
|
11
11
|
import { resolveAndIncludeFromMemory, resolveElements } from './_utils'
|
|
12
12
|
|
|
13
|
-
export const OutgoingExprPredicate: PredicateExecutor<
|
|
13
|
+
export const OutgoingExprPredicate: PredicateExecutor<ModelLayer.RelationExpr.Outgoing> = {
|
|
14
14
|
include: ({ expr, scope, model, memory, stage, filterWhere }) => {
|
|
15
15
|
const target = expr.outgoing
|
|
16
16
|
const connections = [] as ConnectionModel<AnyAux>[]
|
|
17
|
-
if (
|
|
17
|
+
if (ModelLayer.FqnExpr.isWildcard(target)) {
|
|
18
18
|
if (!scope) {
|
|
19
19
|
return
|
|
20
20
|
}
|
|
@@ -23,8 +23,8 @@ export const OutgoingExprPredicate: PredicateExecutor<Expr.OutgoingExpr> = {
|
|
|
23
23
|
...findConnectionsBetween(
|
|
24
24
|
scope,
|
|
25
25
|
scope.ascendingSiblings(),
|
|
26
|
-
'directed'
|
|
27
|
-
)
|
|
26
|
+
'directed',
|
|
27
|
+
),
|
|
28
28
|
)
|
|
29
29
|
} else {
|
|
30
30
|
const elements = resolveAndIncludeFromMemory(target, { memory, model })
|
|
@@ -32,8 +32,8 @@ export const OutgoingExprPredicate: PredicateExecutor<Expr.OutgoingExpr> = {
|
|
|
32
32
|
if (visibleElements.length === 0) {
|
|
33
33
|
visibleElements.push(
|
|
34
34
|
...unique(
|
|
35
|
-
elements.flatMap(el => [...el.ascendingSiblings()])
|
|
36
|
-
)
|
|
35
|
+
elements.flatMap(el => [...el.ascendingSiblings()]),
|
|
36
|
+
),
|
|
37
37
|
)
|
|
38
38
|
}
|
|
39
39
|
const ensureOutgoing = outgoingConnectionPredicate(model, target)
|
|
@@ -43,21 +43,21 @@ export const OutgoingExprPredicate: PredicateExecutor<Expr.OutgoingExpr> = {
|
|
|
43
43
|
...findConnectionsBetween(
|
|
44
44
|
source,
|
|
45
45
|
visibleElements,
|
|
46
|
-
'directed'
|
|
47
|
-
).filter(ensureOutgoing)
|
|
46
|
+
'directed',
|
|
47
|
+
).filter(ensureOutgoing),
|
|
48
48
|
)
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
stage.addConnections(
|
|
53
|
-
filterWhere(connections)
|
|
53
|
+
filterWhere(connections),
|
|
54
54
|
)
|
|
55
55
|
|
|
56
56
|
return stage
|
|
57
57
|
},
|
|
58
58
|
exclude: ({ expr: { outgoing }, model, scope, stage, where }) => {
|
|
59
59
|
const excluded = [] as RelationshipModel[]
|
|
60
|
-
if (
|
|
60
|
+
if (ModelLayer.FqnExpr.isWildcard(outgoing)) {
|
|
61
61
|
if (!scope) {
|
|
62
62
|
return
|
|
63
63
|
}
|
|
@@ -65,50 +65,54 @@ export const OutgoingExprPredicate: PredicateExecutor<Expr.OutgoingExpr> = {
|
|
|
65
65
|
} else {
|
|
66
66
|
const elements = resolveElements(model, outgoing)
|
|
67
67
|
excluded.push(
|
|
68
|
-
...elements.flatMap(e => [...e.allOutgoing])
|
|
68
|
+
...elements.flatMap(e => [...e.allOutgoing]),
|
|
69
69
|
)
|
|
70
70
|
}
|
|
71
71
|
stage.excludeRelations(
|
|
72
|
-
toSet(excluded.filter(where))
|
|
72
|
+
toSet(excluded.filter(where)),
|
|
73
73
|
)
|
|
74
74
|
|
|
75
75
|
return stage
|
|
76
|
-
}
|
|
76
|
+
},
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
export function outgoingConnectionPredicate(
|
|
80
80
|
model: LikeC4Model,
|
|
81
|
-
expr:
|
|
81
|
+
expr: ModelLayer.FqnExpr.NonWildcard,
|
|
82
82
|
): ConnectionWhere {
|
|
83
83
|
switch (true) {
|
|
84
|
-
case
|
|
85
|
-
case
|
|
84
|
+
case ModelLayer.FqnExpr.isElementKindExpr(expr):
|
|
85
|
+
case ModelLayer.FqnExpr.isElementTagExpr(expr): {
|
|
86
86
|
const isElement = elementExprToPredicate(expr)
|
|
87
87
|
return (connection) => isElement(connection.source)
|
|
88
88
|
}
|
|
89
|
-
case
|
|
89
|
+
case ModelLayer.FqnExpr.isModelRef(expr) && expr.selector === 'children': {
|
|
90
|
+
const fqn = ModelLayer.FqnRef.toFqn(expr.ref)
|
|
90
91
|
return anyPass(
|
|
91
|
-
[...model.children(
|
|
92
|
-
el => Connection.isOutgoing(el.id)
|
|
93
|
-
)
|
|
92
|
+
[...model.children(fqn)].map(
|
|
93
|
+
el => Connection.isOutgoing(el.id),
|
|
94
|
+
),
|
|
94
95
|
)
|
|
95
96
|
}
|
|
96
|
-
case
|
|
97
|
+
case ModelLayer.FqnExpr.isModelRef(expr) && expr.selector === 'descendants': {
|
|
98
|
+
const fqn = ModelLayer.FqnRef.toFqn(expr.ref)
|
|
97
99
|
return anyPass([
|
|
98
|
-
Connection.isInside(
|
|
99
|
-
...[...model.children(
|
|
100
|
-
el => Connection.isOutgoing(el.id)
|
|
101
|
-
)
|
|
100
|
+
Connection.isInside(fqn),
|
|
101
|
+
...[...model.children(fqn)].map(
|
|
102
|
+
el => Connection.isOutgoing(el.id),
|
|
103
|
+
),
|
|
102
104
|
])
|
|
103
105
|
}
|
|
104
|
-
case
|
|
106
|
+
case ModelLayer.FqnExpr.isModelRef(expr) && expr.selector === 'expanded': {
|
|
107
|
+
const fqn = ModelLayer.FqnRef.toFqn(expr.ref)
|
|
105
108
|
return anyPass([
|
|
106
|
-
Connection.isOutgoing(
|
|
107
|
-
Connection.isInside(
|
|
109
|
+
Connection.isOutgoing(fqn),
|
|
110
|
+
Connection.isInside(fqn),
|
|
108
111
|
])
|
|
109
112
|
}
|
|
110
|
-
case
|
|
111
|
-
|
|
113
|
+
case ModelLayer.FqnExpr.isModelRef(expr): {
|
|
114
|
+
const fqn = ModelLayer.FqnRef.toFqn(expr.ref)
|
|
115
|
+
return Connection.isOutgoing(fqn)
|
|
112
116
|
}
|
|
113
117
|
default:
|
|
114
118
|
nonexhaustive(expr)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { filter } from 'remeda'
|
|
2
2
|
import { findConnectionsBetween, findConnectionsWithin } from '../../../model/connection/model'
|
|
3
|
-
import type {
|
|
3
|
+
import type { ModelLayer } from '../../../types/expression-v2-model'
|
|
4
4
|
import { ifilter, toArray } from '../../../utils/iterable'
|
|
5
5
|
import { toSet } from '../../../utils/iterable/to'
|
|
6
6
|
import { type PredicateExecutor, Memory } from '../_types'
|
|
7
7
|
import { NoWhere } from '../utils'
|
|
8
8
|
|
|
9
|
-
export const WildcardPredicate: PredicateExecutor<
|
|
9
|
+
export const WildcardPredicate: PredicateExecutor<ModelLayer.FqnExpr.Wildcard> = {
|
|
10
10
|
include: ({ scope, model, stage, memory, where }) => {
|
|
11
11
|
if (!scope) {
|
|
12
12
|
const rootElements = [...model.roots()].filter(where)
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { isEmpty, isNullish, omitBy } from 'remeda'
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
type ViewRule,
|
|
4
|
+
ComputedNode,
|
|
5
|
+
isViewRuleGroup,
|
|
6
|
+
isViewRulePredicate,
|
|
7
|
+
ModelLayer,
|
|
8
|
+
} from '../../types'
|
|
4
9
|
import { elementExprToPredicate } from './elementExpressionToPredicate'
|
|
5
10
|
|
|
6
|
-
export function flattenGroupRules<T extends Expression>(guard: (expr: Expression) => expr is T) {
|
|
11
|
+
export function flattenGroupRules<T extends ModelLayer.Expression>(guard: (expr: ModelLayer.Expression) => expr is T) {
|
|
7
12
|
return (rule: ViewRule): Array<T> => {
|
|
8
13
|
if (isViewRuleGroup(rule)) {
|
|
9
14
|
return rule.groupRules.flatMap(flattenGroupRules(guard))
|
|
@@ -17,7 +22,7 @@ export function flattenGroupRules<T extends Expression>(guard: (expr: Expression
|
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
export function applyCustomElementProperties(_rules: ViewRule[], _nodes: ComputedNode[]) {
|
|
20
|
-
const rules = _rules.flatMap(flattenGroupRules(
|
|
25
|
+
const rules = _rules.flatMap(flattenGroupRules(ModelLayer.FqnExpr.isCustom))
|
|
21
26
|
if (rules.length === 0) {
|
|
22
27
|
return _nodes
|
|
23
28
|
}
|
|
@@ -1,26 +1,29 @@
|
|
|
1
1
|
import { isNullish, omitBy, pick } from 'remeda'
|
|
2
|
-
import type
|
|
3
|
-
import * as Expr from '../../types/expression'
|
|
2
|
+
import { type ComputedEdge, type ComputedNode, type ViewRule, ModelLayer } from '../../types'
|
|
4
3
|
import { flattenGroupRules } from './applyCustomElementProperties'
|
|
5
4
|
import { relationExpressionToPredicates } from './relationExpressionToPredicates'
|
|
6
5
|
|
|
7
6
|
export function applyCustomRelationProperties(
|
|
8
7
|
_rules: ViewRule[],
|
|
9
8
|
nodes: ComputedNode[],
|
|
10
|
-
_edges: Iterable<ComputedEdge
|
|
9
|
+
_edges: Iterable<ComputedEdge>,
|
|
11
10
|
): ComputedEdge[] {
|
|
12
|
-
const rules = _rules.flatMap(flattenGroupRules(
|
|
11
|
+
const rules = _rules.flatMap(flattenGroupRules(ModelLayer.RelationExpr.isCustom))
|
|
13
12
|
const edges = Array.from(_edges)
|
|
14
13
|
if (rules.length === 0 || edges.length === 0) {
|
|
15
14
|
return edges
|
|
16
15
|
}
|
|
17
16
|
for (
|
|
18
17
|
const {
|
|
19
|
-
customRelation: {
|
|
18
|
+
customRelation: {
|
|
19
|
+
expr,
|
|
20
|
+
title,
|
|
21
|
+
...customprops
|
|
22
|
+
},
|
|
20
23
|
} of rules
|
|
21
24
|
) {
|
|
22
25
|
const props = omitBy(customprops, isNullish)
|
|
23
|
-
const satisfies = relationExpressionToPredicates(
|
|
26
|
+
const satisfies = relationExpressionToPredicates(expr)
|
|
24
27
|
edges.forEach((edge, i) => {
|
|
25
28
|
const source = nodes.find(n => n.id === edge.source)
|
|
26
29
|
const target = nodes.find(n => n.id === edge.target)
|
|
@@ -32,7 +35,7 @@ export function applyCustomRelationProperties(
|
|
|
32
35
|
...edge,
|
|
33
36
|
...props,
|
|
34
37
|
label: title ?? edge.label,
|
|
35
|
-
isCustomized: true
|
|
38
|
+
isCustomized: true,
|
|
36
39
|
}
|
|
37
40
|
}
|
|
38
41
|
})
|
|
@@ -1,50 +1,51 @@
|
|
|
1
1
|
import { isNullish } from 'remeda'
|
|
2
2
|
import { nonexhaustive } from '../../errors'
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
isElementKindExpr,
|
|
7
|
-
isElementRef,
|
|
8
|
-
isElementTagExpr,
|
|
9
|
-
isElementWhere,
|
|
10
|
-
isExpandedElementExpr,
|
|
11
|
-
isWildcard,
|
|
12
|
-
whereOperatorAsPredicate
|
|
4
|
+
ModelLayer,
|
|
5
|
+
whereOperatorAsPredicate,
|
|
13
6
|
} from '../../types'
|
|
14
7
|
import { parentFqn } from '../../utils'
|
|
15
8
|
|
|
16
9
|
type Predicate<T> = (x: T) => boolean
|
|
17
10
|
|
|
18
11
|
export function elementExprToPredicate<T extends { id: string; tags?: readonly string[] | null; kind: string }>(
|
|
19
|
-
target:
|
|
12
|
+
target: ModelLayer.AnyFqnExpr,
|
|
20
13
|
): Predicate<T> {
|
|
21
|
-
if (
|
|
14
|
+
if (ModelLayer.FqnExpr.isCustom(target)) {
|
|
15
|
+
return elementExprToPredicate(target.custom.expr)
|
|
16
|
+
}
|
|
17
|
+
if (ModelLayer.FqnExpr.isWhere(target)) {
|
|
22
18
|
const predicate = elementExprToPredicate(target.where.expr)
|
|
23
19
|
const where = whereOperatorAsPredicate(target.where.condition)
|
|
24
20
|
return n => predicate(n) && where(n)
|
|
25
21
|
}
|
|
26
|
-
if (
|
|
27
|
-
return () => true
|
|
28
|
-
}
|
|
29
|
-
if (isElementKindExpr(target)) {
|
|
22
|
+
if (ModelLayer.FqnExpr.isElementKindExpr(target)) {
|
|
30
23
|
return target.isEqual ? n => n.kind === target.elementKind : n => n.kind !== target.elementKind
|
|
31
24
|
}
|
|
32
|
-
if (isElementTagExpr(target)) {
|
|
25
|
+
if (ModelLayer.FqnExpr.isElementTagExpr(target)) {
|
|
33
26
|
return target.isEqual
|
|
34
27
|
? ({ tags }) => !!tags && tags.includes(target.elementTag)
|
|
35
28
|
: ({ tags }) => isNullish(tags) || !tags.includes(target.elementTag)
|
|
36
29
|
}
|
|
37
|
-
if (
|
|
38
|
-
return
|
|
39
|
-
}
|
|
40
|
-
if (isElementRef(target)) {
|
|
41
|
-
const { element, isChildren, isDescendants } = target
|
|
42
|
-
return isChildren || isDescendants
|
|
43
|
-
? n => n.id.startsWith(element + '.')
|
|
44
|
-
: n => (n.id as string) === element
|
|
30
|
+
if (ModelLayer.FqnExpr.isWildcard(target)) {
|
|
31
|
+
return () => true
|
|
45
32
|
}
|
|
46
|
-
if (
|
|
47
|
-
|
|
33
|
+
if (ModelLayer.FqnExpr.isModelRef(target)) {
|
|
34
|
+
const fqn = ModelLayer.FqnRef.toFqn(target.ref)
|
|
35
|
+
if (target.selector === 'expanded') {
|
|
36
|
+
return (n) => {
|
|
37
|
+
return n.id === fqn || parentFqn(n.id) === fqn
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (target.selector === 'descendants' || target.selector === 'children') {
|
|
41
|
+
const fqnWithDot = fqn + '.'
|
|
42
|
+
return (n) => {
|
|
43
|
+
return n.id.startsWith(fqnWithDot)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return (n) => {
|
|
47
|
+
return n.id === fqn
|
|
48
|
+
}
|
|
48
49
|
}
|
|
49
50
|
nonexhaustive(target)
|
|
50
51
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { last, reverse } from 'remeda'
|
|
2
2
|
import { invariant } from '../../errors'
|
|
3
|
-
import type { Fqn } from '../../types
|
|
3
|
+
import type { Fqn } from '../../types'
|
|
4
4
|
import type { ComputedEdge, ComputedNode } from '../../types/view'
|
|
5
5
|
import { commonHead } from '../../utils/common-head'
|
|
6
6
|
import { ancestorsOfNode } from './ancestorsOfNode'
|
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
import { pick } from 'remeda'
|
|
2
1
|
import { nonexhaustive } from '../../errors'
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
type RelationExpression,
|
|
6
|
-
type RelationWhereExpr,
|
|
7
|
-
isIncoming,
|
|
8
|
-
isInOut,
|
|
9
|
-
isOutgoing,
|
|
10
|
-
isRelation,
|
|
11
|
-
isRelationWhere,
|
|
12
|
-
} from '../../types/expression'
|
|
2
|
+
import { ModelLayer } from '../../types'
|
|
13
3
|
import { whereOperatorAsPredicate } from '../../types/operators'
|
|
14
4
|
import type { ModelRelation } from '../../types/relation'
|
|
15
5
|
import type { ComputedNode } from '../../types/view'
|
|
@@ -22,10 +12,13 @@ export type FilterableEdge = Pick<ModelRelation, 'kind' | 'tags'> & {
|
|
|
22
12
|
}
|
|
23
13
|
|
|
24
14
|
export function relationExpressionToPredicates<T extends FilterableEdge>(
|
|
25
|
-
expr:
|
|
15
|
+
expr: ModelLayer.AnyRelationExpr,
|
|
26
16
|
): Predicate<T> {
|
|
27
17
|
switch (true) {
|
|
28
|
-
case
|
|
18
|
+
case ModelLayer.RelationExpr.isCustom(expr): {
|
|
19
|
+
return relationExpressionToPredicates(expr.customRelation.expr)
|
|
20
|
+
}
|
|
21
|
+
case ModelLayer.RelationExpr.isWhere(expr): {
|
|
29
22
|
const predicate = relationExpressionToPredicates(expr.where.expr)
|
|
30
23
|
const where = whereOperatorAsPredicate(expr.where.condition)
|
|
31
24
|
return e =>
|
|
@@ -36,7 +29,7 @@ export function relationExpressionToPredicates<T extends FilterableEdge>(
|
|
|
36
29
|
...(e.kind && { kind: e.kind }),
|
|
37
30
|
})
|
|
38
31
|
}
|
|
39
|
-
case
|
|
32
|
+
case ModelLayer.RelationExpr.isDirect(expr): {
|
|
40
33
|
const isSource = elementExprToPredicate(expr.source)
|
|
41
34
|
const isTarget = elementExprToPredicate(expr.target)
|
|
42
35
|
return edge => {
|
|
@@ -44,15 +37,15 @@ export function relationExpressionToPredicates<T extends FilterableEdge>(
|
|
|
44
37
|
|| (!!expr.isBidirectional && isSource(edge.target) && isTarget(edge.source))
|
|
45
38
|
}
|
|
46
39
|
}
|
|
47
|
-
case isInOut(expr): {
|
|
40
|
+
case ModelLayer.RelationExpr.isInOut(expr): {
|
|
48
41
|
const isInOut = elementExprToPredicate(expr.inout)
|
|
49
42
|
return edge => isInOut(edge.source) || isInOut(edge.target)
|
|
50
43
|
}
|
|
51
|
-
case isIncoming(expr): {
|
|
44
|
+
case ModelLayer.RelationExpr.isIncoming(expr): {
|
|
52
45
|
const isTarget = elementExprToPredicate(expr.incoming)
|
|
53
46
|
return edge => isTarget(edge.target)
|
|
54
47
|
}
|
|
55
|
-
case isOutgoing(expr): {
|
|
48
|
+
case ModelLayer.RelationExpr.isOutgoing(expr): {
|
|
56
49
|
const isSource = elementExprToPredicate(expr.outgoing)
|
|
57
50
|
return edge => isSource(edge.source)
|
|
58
51
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { flatMap, hasAtLeast, pipe, sort, unique } from 'remeda'
|
|
2
2
|
import type { LiteralUnion } from 'type-fest'
|
|
3
3
|
import type { NonEmptyReadonlyArray } from '../../types/_common'
|
|
4
|
-
import type { Tag } from '../../types/
|
|
4
|
+
import type { Tag } from '../../types/scalars'
|
|
5
5
|
import { compareNatural } from '../../utils/compare-natural'
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -9,13 +9,13 @@ import { compareNatural } from '../../utils/compare-natural'
|
|
|
9
9
|
* and sort in natural order; returns null if no tags are present.
|
|
10
10
|
*/
|
|
11
11
|
export function uniqueTags<T extends { tags?: NonEmptyReadonlyArray<LiteralUnion<Tag, string>> | null }>(
|
|
12
|
-
elements: ReadonlyArray<T
|
|
12
|
+
elements: ReadonlyArray<T>,
|
|
13
13
|
) {
|
|
14
14
|
const tags = pipe(
|
|
15
15
|
elements,
|
|
16
16
|
flatMap(e => e.tags ?? []),
|
|
17
17
|
unique(),
|
|
18
|
-
sort(compareNatural)
|
|
18
|
+
sort(compareNatural),
|
|
19
19
|
)
|
|
20
20
|
return hasAtLeast(tags, 1) ? tags as NonEmptyReadonlyArray<Tag> : null
|
|
21
21
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { isTruthy } from 'remeda'
|
|
2
2
|
import type { SetRequired } from 'type-fest'
|
|
3
|
+
import type { IteratorLike } from '../types/_common'
|
|
3
4
|
import {
|
|
4
5
|
type Element as C4Element,
|
|
5
6
|
type ElementKind as C4ElementKind,
|
|
6
7
|
type ElementShape as C4ElementShape,
|
|
7
8
|
type ElementStyle,
|
|
8
|
-
type IconUrl,
|
|
9
|
-
type IteratorLike,
|
|
10
9
|
type Link,
|
|
11
|
-
type Tag as C4Tag,
|
|
12
|
-
type ThemeColor,
|
|
13
10
|
DefaultElementShape,
|
|
14
11
|
DefaultThemeColor,
|
|
15
|
-
} from '../types'
|
|
12
|
+
} from '../types/element'
|
|
16
13
|
import { DefaultShapeSize } from '../types/element'
|
|
14
|
+
import {
|
|
15
|
+
type IconUrl,
|
|
16
|
+
type ProjectId,
|
|
17
|
+
type Tag as C4Tag,
|
|
18
|
+
splitGlobalFqn,
|
|
19
|
+
} from '../types/scalars'
|
|
20
|
+
import type { ThemeColor } from '../types/theme'
|
|
17
21
|
import { commonAncestor, hierarchyLevel, isAncestor, sortNaturalByFqn } from '../utils'
|
|
18
22
|
import { type DeployedInstancesIterator } from './DeploymentElementModel'
|
|
19
23
|
import type { LikeC4Model } from './LikeC4Model'
|
|
@@ -28,13 +32,28 @@ export class ElementModel<M extends AnyAux = AnyAux> {
|
|
|
28
32
|
readonly _literalId: M['Element']
|
|
29
33
|
readonly hierarchyLevel: number
|
|
30
34
|
|
|
35
|
+
readonly imported: null | {
|
|
36
|
+
from: ProjectId
|
|
37
|
+
fqn: string
|
|
38
|
+
}
|
|
39
|
+
|
|
31
40
|
constructor(
|
|
32
41
|
public readonly $model: LikeC4Model<M>,
|
|
33
42
|
public readonly $element: C4Element,
|
|
34
43
|
) {
|
|
35
44
|
this.id = this.$element.id
|
|
36
45
|
this._literalId = this.$element.id
|
|
37
|
-
|
|
46
|
+
const [projectId, fqn] = splitGlobalFqn(this.id)
|
|
47
|
+
if (projectId) {
|
|
48
|
+
this.imported = {
|
|
49
|
+
from: projectId,
|
|
50
|
+
fqn,
|
|
51
|
+
}
|
|
52
|
+
this.hierarchyLevel = hierarchyLevel(fqn)
|
|
53
|
+
} else {
|
|
54
|
+
this.imported = null
|
|
55
|
+
this.hierarchyLevel = hierarchyLevel(this.id)
|
|
56
|
+
}
|
|
38
57
|
}
|
|
39
58
|
|
|
40
59
|
get parent(): ElementModel<M> | null {
|
package/src/model/LikeC4Model.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import { mapValues, pipe, sort, values } from 'remeda'
|
|
1
|
+
import { entries, flat, mapValues, pipe, sort, values } from 'remeda'
|
|
2
2
|
import type { LiteralUnion } from 'type-fest'
|
|
3
3
|
import { computeView, unsafeComputeView } from '../compute-view'
|
|
4
4
|
import type { ComputeViewResult } from '../compute-view/compute-view'
|
|
5
5
|
import { invariant, nonNullable } from '../errors'
|
|
6
|
-
import type {
|
|
6
|
+
import type { IteratorLike } from '../types/_common'
|
|
7
7
|
import type { Element } from '../types/element'
|
|
8
|
-
import
|
|
8
|
+
import type { ModelGlobals } from '../types/global'
|
|
9
9
|
import type { AnyParsedLikeC4ModelData, GenericLikeC4ModelData, LikeC4ModelDump } from '../types/model-data'
|
|
10
10
|
import type { ModelRelation } from '../types/relation'
|
|
11
|
+
import { type ProjectId, type Tag as C4Tag, GlobalFqn, isGlobalFqn } from '../types/scalars'
|
|
12
|
+
import type { ComputedView, DiagramView, LikeC4View } from '../types/view'
|
|
11
13
|
import { compareNatural } from '../utils'
|
|
12
|
-
import { ancestorsFqn, commonAncestor, parentFqn } from '../utils/fqn'
|
|
14
|
+
import { ancestorsFqn, commonAncestor, parentFqn, sortParentsFirst } from '../utils/fqn'
|
|
13
15
|
import { DefaultMap } from '../utils/mnemonist'
|
|
14
16
|
import type {
|
|
15
17
|
DeployedInstanceModel,
|
|
@@ -121,6 +123,11 @@ export class LikeC4Model<M extends AnyAux = LikeC4Model.Any> {
|
|
|
121
123
|
this.#allTags.get(tag).add(el)
|
|
122
124
|
}
|
|
123
125
|
}
|
|
126
|
+
for (const [projectId, elements] of entries($model.imports)) {
|
|
127
|
+
for (const element of sortParentsFirst(elements)) {
|
|
128
|
+
this.addImportedElement(projectId as ProjectId, element)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
124
131
|
for (const relation of values($model.relations)) {
|
|
125
132
|
const el = this.addRelation(relation)
|
|
126
133
|
for (const tag of el.tags) {
|
|
@@ -365,7 +372,7 @@ export class LikeC4Model<M extends AnyAux = LikeC4Model.Any> {
|
|
|
365
372
|
if (this.#elements.has(element.id)) {
|
|
366
373
|
throw new Error(`Element ${element.id} already exists`)
|
|
367
374
|
}
|
|
368
|
-
const el = new ElementModel(this, Object.freeze(
|
|
375
|
+
const el = new ElementModel(this, Object.freeze(element))
|
|
369
376
|
this.#elements.set(el.id, el)
|
|
370
377
|
const parentId = parentFqn(el.id)
|
|
371
378
|
if (parentId) {
|
|
@@ -378,13 +385,42 @@ export class LikeC4Model<M extends AnyAux = LikeC4Model.Any> {
|
|
|
378
385
|
return el
|
|
379
386
|
}
|
|
380
387
|
|
|
388
|
+
private addImportedElement(projectId: ProjectId, element: Element) {
|
|
389
|
+
invariant(!isGlobalFqn(element.id), `Imported element already has global FQN`)
|
|
390
|
+
const id = GlobalFqn(projectId, element.id)
|
|
391
|
+
if (this.#elements.has(id)) {
|
|
392
|
+
throw new Error(`Element ${id} already exists`)
|
|
393
|
+
}
|
|
394
|
+
const el = new ElementModel(
|
|
395
|
+
this,
|
|
396
|
+
Object.freeze({
|
|
397
|
+
...element,
|
|
398
|
+
id,
|
|
399
|
+
}),
|
|
400
|
+
)
|
|
401
|
+
this.#elements.set(el.id, el)
|
|
402
|
+
let parentId = parentFqn(el.id)
|
|
403
|
+
while (parentId) {
|
|
404
|
+
// For imported elements - id has format `@projectId.fqn`
|
|
405
|
+
// We need to exclude `@projectId` from the parentId
|
|
406
|
+
if (parentId.includes('.') && this.#elements.has(parentId)) {
|
|
407
|
+
this.#parents.set(el.id, this.element(parentId))
|
|
408
|
+
this.#children.get(parentId).add(el)
|
|
409
|
+
return el
|
|
410
|
+
}
|
|
411
|
+
parentId = parentFqn(parentId)
|
|
412
|
+
}
|
|
413
|
+
this.#rootElements.add(el)
|
|
414
|
+
return el
|
|
415
|
+
}
|
|
416
|
+
|
|
381
417
|
private addRelation(relation: ModelRelation) {
|
|
382
418
|
if (this.#relations.has(relation.id)) {
|
|
383
419
|
throw new Error(`Relation ${relation.id} already exists`)
|
|
384
420
|
}
|
|
385
421
|
const rel = new RelationshipModel(
|
|
386
422
|
this,
|
|
387
|
-
Object.freeze(
|
|
423
|
+
Object.freeze(relation),
|
|
388
424
|
)
|
|
389
425
|
const { source, target } = rel
|
|
390
426
|
this.#relations.set(rel.id, rel)
|
|
@@ -418,6 +454,7 @@ export class LikeC4Model<M extends AnyAux = LikeC4Model.Any> {
|
|
|
418
454
|
|
|
419
455
|
export namespace LikeC4Model {
|
|
420
456
|
export const EMPTY = LikeC4Model.create({
|
|
457
|
+
projectId: 'default' as ProjectId,
|
|
421
458
|
specification: {
|
|
422
459
|
elements: {},
|
|
423
460
|
relationships: {},
|
|
@@ -436,6 +473,7 @@ export namespace LikeC4Model {
|
|
|
436
473
|
elements: {},
|
|
437
474
|
relations: {},
|
|
438
475
|
views: {},
|
|
476
|
+
imports: {},
|
|
439
477
|
}) as LikeC4Model<AnyAux>
|
|
440
478
|
|
|
441
479
|
export type Any = Aux<
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { isEmpty } from 'remeda'
|
|
2
|
-
import type {
|
|
3
|
-
import type { Link
|
|
2
|
+
import type { IteratorLike } from '../types/_common'
|
|
3
|
+
import type { Link } from '../types/element'
|
|
4
4
|
import {
|
|
5
5
|
type ModelRelation,
|
|
6
6
|
type RelationshipLineType,
|
|
7
7
|
DefaultLineStyle,
|
|
8
8
|
DefaultRelationshipColor,
|
|
9
9
|
} from '../types/relation'
|
|
10
|
+
import type { Tag } from '../types/scalars'
|
|
11
|
+
import type { Color } from '../types/theme'
|
|
10
12
|
import { commonAncestor } from '../utils/fqn'
|
|
11
13
|
import type { DeploymentRelationModel } from './DeploymentElementModel'
|
|
12
14
|
import type { ElementModel } from './ElementModel'
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { nonNullable } from '../../errors'
|
|
2
|
-
import type { IteratorLike } from '../../types'
|
|
3
|
-
import type { Link
|
|
2
|
+
import type { IteratorLike } from '../../types/_common'
|
|
3
|
+
import type { Link } from '../../types/element'
|
|
4
|
+
import type { Tag } from '../../types/scalars'
|
|
4
5
|
import {
|
|
5
6
|
type ComputedDeploymentView,
|
|
6
7
|
type ComputedDynamicView,
|
package/src/types/_common.ts
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import type { Simplify, Tagged, UnionToIntersection } from 'type-fest'
|
|
2
2
|
|
|
3
|
-
export type ProjectId = Tagged<string, 'ProjectID'>
|
|
4
|
-
|
|
5
3
|
export type NonEmptyArray<T> = [T, ...T[]]
|
|
6
4
|
|
|
7
5
|
export type NonEmptyReadonlyArray<T> = readonly [T, ...T[]]
|
|
8
6
|
|
|
9
|
-
export type IconUrl = Tagged<string, 'IconUrl'> | 'none'
|
|
10
|
-
|
|
11
7
|
export type CustomColor = string
|
|
12
8
|
|
|
13
9
|
export type Point = readonly [x: number, y: number]
|
package/src/types/deployments.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { MergeExclusive, Simplify, Tagged } from 'type-fest'
|
|
2
|
-
import type {
|
|
3
|
-
import type { ElementShape, ElementStyle,
|
|
2
|
+
import type { NonEmptyArray } from './_common'
|
|
3
|
+
import type { ElementShape, ElementStyle, Link } from './element'
|
|
4
4
|
import type { AbstractRelation, RelationId } from './relation'
|
|
5
|
+
import type { Fqn, IconUrl, Tag } from './scalars'
|
|
5
6
|
import type { Color } from './theme'
|
|
6
7
|
|
|
7
8
|
export type DeploymentNodeKind<Kinds extends string = string> = Tagged<Kinds, 'DeploymentNodeKind'>
|
package/src/types/element.ts
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
import type { Tagged, TupleToUnion } from 'type-fest'
|
|
2
|
-
import type {
|
|
2
|
+
import type { NonEmptyArray } from './_common'
|
|
3
|
+
import type { Fqn, IconUrl, Tag } from './scalars'
|
|
3
4
|
import type { Color, ShapeSize, SpacingSize, TextSize, ThemeColor } from './theme'
|
|
4
5
|
|
|
5
|
-
// Full-qualified-name
|
|
6
|
-
export type Fqn<Id extends string = string> = Tagged<Id, 'Fqn'>
|
|
7
|
-
|
|
8
|
-
export function AsFqn(name: string, parent?: Fqn | null) {
|
|
9
|
-
return (parent ? parent + '.' + name : name) as Fqn
|
|
10
|
-
}
|
|
11
|
-
|
|
12
6
|
export const BorderStyles = ['solid', 'dashed', 'dotted', 'none'] as const
|
|
13
7
|
|
|
14
8
|
export type BorderStyle = TupleToUnion<typeof BorderStyles>
|
|
@@ -60,8 +54,6 @@ export interface ElementStyle {
|
|
|
60
54
|
readonly textSize?: TextSize
|
|
61
55
|
}
|
|
62
56
|
|
|
63
|
-
export type Tag<Tags extends string = string> = Tagged<Tags, 'Tag'>
|
|
64
|
-
|
|
65
57
|
export interface TagSpec {
|
|
66
58
|
readonly id: Tag
|
|
67
59
|
readonly style: ElementStyle
|