@likec4/core 1.19.1 → 1.20.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.
Files changed (91) hide show
  1. package/dist/builder/index.cjs +881 -0
  2. package/dist/builder/index.d.cts +503 -0
  3. package/dist/builder/index.d.mts +5 -4
  4. package/dist/builder/index.d.ts +5 -4
  5. package/dist/builder/{index.js → index.mjs} +14 -4
  6. package/dist/compute-view/index.cjs +86 -0
  7. package/dist/compute-view/index.d.cts +23 -0
  8. package/dist/compute-view/index.d.mts +3 -2
  9. package/dist/compute-view/index.d.ts +3 -2
  10. package/dist/compute-view/{index.js → index.mjs} +6 -5
  11. package/dist/index.cjs +4320 -0
  12. package/dist/index.d.cts +270 -0
  13. package/dist/index.d.mts +130 -4
  14. package/dist/index.d.ts +130 -4
  15. package/dist/{index.js → index.mjs} +512 -7
  16. package/dist/model/index.cjs +40 -0
  17. package/dist/model/index.d.cts +259 -0
  18. package/dist/model/index.d.mts +8 -3
  19. package/dist/model/index.d.ts +8 -3
  20. package/dist/model/index.mjs +1 -0
  21. package/dist/shared/core.B2QYWlYe.cjs +435 -0
  22. package/dist/shared/core.B72vUaNB.d.cts +850 -0
  23. package/dist/shared/core.BGus30EM.cjs +8027 -0
  24. package/dist/shared/core.BRuJ3Wzf.d.mts +12 -0
  25. package/dist/shared/{core.BZ9Msq7w.d.mts → core.BdbFkE_p.d.cts} +14 -4
  26. package/dist/shared/core.BvdNMbcA.d.cts +12 -0
  27. package/dist/shared/{object_hash.CE_oF3I3.js → core.BxvDITEI.mjs} +26 -389
  28. package/dist/shared/core.ByNgh-mC.d.cts +127 -0
  29. package/dist/shared/core.C9DCnJh8.cjs +908 -0
  30. package/dist/shared/{index.DafVOuVd.js → core.CqCTxzMY.mjs} +1111 -572
  31. package/dist/shared/core.D74xkKkG.d.cts +148 -0
  32. package/dist/shared/{core.C05RDLhi.d.ts → core.D_Gc58Gt.d.ts} +23 -5
  33. package/dist/shared/core.Damrzla-.d.ts +12 -0
  34. package/dist/shared/{core.CmYMqgha.d.mts → core.Dfzrh1VA.d.mts} +23 -5
  35. package/dist/shared/core.DsoCu540.cjs +26 -0
  36. package/dist/shared/{view.CyZrG8BJ.js → core.DunIMHRf.mjs} +27 -1
  37. package/dist/shared/{core.BIfgabEw.d.ts → core.ZWiGANIJ.d.ts} +14 -4
  38. package/dist/shared/core.gv1bMwCC.cjs +39 -0
  39. package/dist/shared/core.jy6z2iRn.d.mts +729 -0
  40. package/dist/shared/core.m3rCQAv6.mjs +37 -0
  41. package/dist/types/index.cjs +107 -0
  42. package/dist/types/index.d.cts +72 -0
  43. package/dist/types/index.d.mts +2 -2
  44. package/dist/types/index.d.ts +2 -2
  45. package/dist/types/{index.js → index.mjs} +1 -1
  46. package/dist/utils/index.cjs +95 -0
  47. package/dist/utils/index.d.cts +356 -0
  48. package/dist/utils/index.d.mts +1 -64
  49. package/dist/utils/index.d.ts +1 -64
  50. package/dist/utils/index.mjs +56 -0
  51. package/package.json +52 -26
  52. package/src/builder/Builder.view-common.ts +21 -1
  53. package/src/compute-view/deployment-view/__test__/fixture.ts +31 -3
  54. package/src/compute-view/deployment-view/_types.ts +2 -1
  55. package/src/compute-view/deployment-view/compute.ts +4 -36
  56. package/src/compute-view/deployment-view/predicates/index.ts +8 -0
  57. package/src/compute-view/deployment-view/predicates/relation-direct.ts +125 -99
  58. package/src/compute-view/deployment-view/predicates/relation-in-out.ts +16 -8
  59. package/src/compute-view/deployment-view/predicates/relation-incoming.ts +18 -12
  60. package/src/compute-view/deployment-view/predicates/relation-outgoing.ts +16 -9
  61. package/src/compute-view/deployment-view/predicates/relation-where.ts +18 -0
  62. package/src/compute-view/deployment-view/predicates/utils.ts +214 -0
  63. package/src/compute-view/element-view/__test__/fixture.ts +17 -4
  64. package/src/compute-view/element-view/compute.ts +3 -2
  65. package/src/compute-view/element-view/predicates.ts +520 -0
  66. package/src/compute-view/element-view/utils.ts +11 -7
  67. package/src/compute-view/index.ts +1 -0
  68. package/src/compute-view/relationships-view/_types.ts +16 -0
  69. package/src/compute-view/relationships-view/compute.ts +74 -0
  70. package/src/compute-view/relationships-view/layout.ts +357 -0
  71. package/src/compute-view/relationships-view/utils.ts +40 -0
  72. package/src/compute-view/utils/relationExpressionToPredicates.ts +11 -4
  73. package/src/index.ts +16 -0
  74. package/src/model/DeploymentElementModel.ts +18 -1
  75. package/src/model/ElementModel.ts +13 -0
  76. package/src/model/LikeC4Model.ts +2 -2
  77. package/src/model/RelationModel.ts +9 -0
  78. package/src/model/connection/deployment/DeploymentConnectionModel.ts +11 -1
  79. package/src/model/guards.ts +17 -1
  80. package/src/model/view/LikeC4ViewModel.ts +7 -1
  81. package/src/model/view/NodeModel.ts +5 -0
  82. package/src/types/deployments.ts +1 -0
  83. package/src/types/expression-v2.ts +12 -0
  84. package/src/types/index.ts +1 -0
  85. package/src/types/operators.ts +46 -6
  86. package/src/types/view.ts +2 -2
  87. package/src/utils/index.ts +0 -5
  88. package/src/utils/iterable/unique.ts +1 -7
  89. package/dist/model/index.js +0 -1
  90. package/dist/utils/index.js +0 -347
  91. /package/dist/shared/{chunk-RAAYCPUM.M-JWF7SS.js → core.M-JWF7SS.mjs} +0 -0
@@ -1,24 +1,27 @@
1
+ import { filter, pipe } from 'remeda'
1
2
  import { invariant } from '../../../errors'
2
3
  import { findConnectionsBetween } from '../../../model/connection/deployment'
3
4
  import type { RelationshipModel } from '../../../model/RelationModel'
4
5
  import type { AnyAux } from '../../../model/types'
5
- import { FqnExpr, type RelationExpr } from '../../../types/expression-v2'
6
+ import { type RelationExpr, FqnExpr } from '../../../types/expression-v2'
6
7
  import { union } from '../../../utils/set'
7
8
  import type { PredicateExecutor } from '../_types'
8
9
  import { resolveElements, resolveModelElements } from '../utils'
9
- import { excludeModelRelations, resolveAscendingSiblings } from './relation-direct'
10
+ import { resolveAscendingSiblings } from './relation-direct'
10
11
  import { filterIncomingConnections } from './relation-incoming'
11
12
  import { filterOutgoingConnections } from './relation-outgoing'
13
+ import { applyPredicate, excludeModelRelations, matchConnections } from './utils'
12
14
 
13
15
  //
14
16
  export const InOutRelationPredicate: PredicateExecutor<RelationExpr.InOut> = {
15
- include: ({ expr, model, memory, stage }) => {
17
+ include: ({ expr, model, memory, stage, where }) => {
16
18
  const sources = [...memory.elements]
17
19
 
18
20
  if (FqnExpr.isWildcard(expr.inout)) {
19
21
  for (const source of sources) {
20
22
  const targets = [...resolveAscendingSiblings(source)]
21
- stage.addConnections(findConnectionsBetween(source, targets, 'both'))
23
+ const toInclude = matchConnections(findConnectionsBetween(source, targets, 'both'), where)
24
+ stage.addConnections(toInclude)
22
25
  }
23
26
  return stage
24
27
  }
@@ -26,12 +29,13 @@ export const InOutRelationPredicate: PredicateExecutor<RelationExpr.InOut> = {
26
29
 
27
30
  const targets = resolveElements(model, expr.inout)
28
31
  for (const source of sources) {
29
- stage.addConnections(findConnectionsBetween(source, targets, 'both'))
32
+ const toInclude = matchConnections(findConnectionsBetween(source, targets, 'both'), where)
33
+ stage.addConnections(toInclude)
30
34
  }
31
35
 
32
36
  return stage
33
37
  },
34
- exclude: ({ expr, model, memory, stage }) => {
38
+ exclude: ({ expr, model, memory, stage, where }) => {
35
39
  // Exclude all connections that have model relationshps with the elements
36
40
  if (FqnExpr.isModelRef(expr.inout)) {
37
41
  const elements = resolveModelElements(model, expr.inout)
@@ -42,7 +46,7 @@ export const InOutRelationPredicate: PredicateExecutor<RelationExpr.InOut> = {
42
46
  new Set<RelationshipModel<AnyAux>>(),
43
47
  ...elements.flatMap(e => [e.allIncoming, e.allOutgoing]),
44
48
  )
45
- return excludeModelRelations(excludedRelations, { stage, memory })
49
+ return excludeModelRelations(excludedRelations, { stage, memory }, where)
46
50
  }
47
51
 
48
52
  if (FqnExpr.isWildcard(expr.inout)) {
@@ -54,7 +58,11 @@ export const InOutRelationPredicate: PredicateExecutor<RelationExpr.InOut> = {
54
58
  const isIncoming = filterIncomingConnections(elements)
55
59
  const isOutgoing = filterOutgoingConnections(elements)
56
60
 
57
- const toExclude = memory.connections.filter(c => isIncoming(c) !== isOutgoing(c))
61
+ const toExclude = pipe(
62
+ memory.connections,
63
+ filter(c => isIncoming(c) !== isOutgoing(c)),
64
+ applyPredicate(where),
65
+ )
58
66
  stage.excludeConnections(toExclude)
59
67
  return stage
60
68
  },
@@ -1,18 +1,18 @@
1
- import { anyPass } from 'remeda'
1
+ import { anyPass, filter, pipe } from 'remeda'
2
2
  import { invariant } from '../../../errors'
3
+ import type { AnyAux, RelationshipModel } from '../../../model'
3
4
  import { findConnectionsBetween } from '../../../model/connection/deployment'
4
5
  import type { LikeC4DeploymentModel } from '../../../model/DeploymentModel'
5
- import type { RelationshipModel } from '../../../model/RelationModel'
6
- import type { AnyAux } from '../../../model/types'
7
- import { FqnExpr, type RelationExpr } from '../../../types'
8
- import { isAncestor } from '../../../utils/fqn'
6
+ import { type RelationExpr, FqnExpr } from '../../../types'
7
+ import { isAncestor } from '../../../utils'
9
8
  import type { Connection, Elem, PredicateExecutor } from '../_types'
10
9
  import { resolveElements, resolveModelElements } from '../utils'
11
- import { excludeModelRelations, resolveAscendingSiblings } from './relation-direct'
10
+ import { resolveAscendingSiblings } from './relation-direct'
11
+ import { applyPredicate, excludeModelRelations } from './utils'
12
12
 
13
13
  // from visible element incoming to this
14
14
  export const IncomingRelationPredicate: PredicateExecutor<RelationExpr.Incoming> = {
15
- include: ({ expr, model, memory, stage }) => {
15
+ include: ({ expr, model, memory, stage, where }) => {
16
16
  const sources = [...memory.elements]
17
17
 
18
18
  if (FqnExpr.isWildcard(expr.incoming)) {
@@ -21,7 +21,8 @@ export const IncomingRelationPredicate: PredicateExecutor<RelationExpr.Incoming>
21
21
  continue
22
22
  }
23
23
  const targets = [...resolveAscendingSiblings(source)]
24
- stage.addConnections(findConnectionsBetween(source, targets, 'directed'))
24
+ const toInclude = applyPredicate(findConnectionsBetween(source, targets, 'directed'), where)
25
+ stage.addConnections(toInclude)
25
26
  }
26
27
  return stage
27
28
  }
@@ -29,16 +30,17 @@ export const IncomingRelationPredicate: PredicateExecutor<RelationExpr.Incoming>
29
30
 
30
31
  const targets = resolveElements(model, expr.incoming)
31
32
  for (const source of sources) {
32
- stage.addConnections(findConnectionsBetween(source, targets, 'directed'))
33
+ const toInclude = applyPredicate(findConnectionsBetween(source, targets, 'directed'), where)
34
+ stage.addConnections(toInclude)
33
35
  }
34
36
 
35
37
  return stage
36
38
  },
37
- exclude: ({ expr, model, memory, stage }) => {
39
+ exclude: ({ expr, model, memory, stage, where }) => {
38
40
  // Exclude all connections that have model relationshps with the elements
39
41
  if (FqnExpr.isModelRef(expr.incoming)) {
40
42
  const excludedRelations = resolveAllImcomingRelations(model, expr.incoming)
41
- return excludeModelRelations(excludedRelations, { stage, memory })
43
+ return excludeModelRelations(excludedRelations, { stage, memory }, where)
42
44
  }
43
45
  if (FqnExpr.isWildcard(expr.incoming)) {
44
46
  // non-sense
@@ -46,7 +48,11 @@ export const IncomingRelationPredicate: PredicateExecutor<RelationExpr.Incoming>
46
48
  }
47
49
 
48
50
  const isIncoming = filterIncomingConnections(resolveElements(model, expr.incoming))
49
- const toExclude = memory.connections.filter(isIncoming)
51
+ const toExclude = pipe(
52
+ memory.connections,
53
+ filter(isIncoming),
54
+ applyPredicate(where),
55
+ )
50
56
  stage.excludeConnections(toExclude)
51
57
  return stage
52
58
  },
@@ -1,16 +1,17 @@
1
- import { anyPass } from 'remeda'
1
+ import { anyPass, filter, pipe } from 'remeda'
2
2
  import { invariant } from '../../../errors'
3
3
  import type { LikeC4DeploymentModel } from '../../../model'
4
4
  import { findConnection, findConnectionsBetween } from '../../../model/connection/deployment'
5
5
  import type { RelationshipModel } from '../../../model/RelationModel'
6
- import { FqnExpr, type RelationExpr } from '../../../types'
6
+ import { type RelationExpr, FqnExpr } from '../../../types'
7
7
  import { isAncestor } from '../../../utils'
8
8
  import type { Connection, Elem, PredicateExecutor } from '../_types'
9
9
  import { resolveElements, resolveModelElements } from '../utils'
10
- import { excludeModelRelations, resolveAscendingSiblings } from './relation-direct'
10
+ import { resolveAscendingSiblings } from './relation-direct'
11
+ import { applyPredicate, excludeModelRelations } from './utils'
11
12
 
12
13
  export const OutgoingRelationPredicate: PredicateExecutor<RelationExpr.Outgoing> = {
13
- include: ({ expr, model, memory, stage }) => {
14
+ include: ({ expr, model, memory, stage, where }) => {
14
15
  const targets = [...memory.elements]
15
16
 
16
17
  // * -> (to visible elements)
@@ -21,7 +22,8 @@ export const OutgoingRelationPredicate: PredicateExecutor<RelationExpr.Outgoing>
21
22
  continue
22
23
  }
23
24
  for (const source of resolveAscendingSiblings(target)) {
24
- stage.addConnections(findConnection(source, target, 'directed'))
25
+ const toInclude = applyPredicate(findConnection(source, target, 'directed'), where)
26
+ stage.addConnections(toInclude)
25
27
  }
26
28
  }
27
29
  return stage
@@ -30,16 +32,17 @@ export const OutgoingRelationPredicate: PredicateExecutor<RelationExpr.Outgoing>
30
32
 
31
33
  const sources = resolveElements(model, expr.outgoing)
32
34
  for (const source of sources) {
33
- stage.addConnections(findConnectionsBetween(source, targets, 'directed'))
35
+ const toInclude = applyPredicate(findConnectionsBetween(source, targets, 'directed'), where)
36
+ stage.addConnections(toInclude)
34
37
  }
35
38
 
36
39
  return stage
37
40
  },
38
- exclude: ({ expr, model, memory, stage }) => {
41
+ exclude: ({ expr, model, memory, stage, where }) => {
39
42
  // Exclude all connections that have model relationshps with the elements
40
43
  if (FqnExpr.isModelRef(expr.outgoing)) {
41
44
  const excludedRelations = resolveAllOutgoingRelations(model, expr.outgoing)
42
- return excludeModelRelations(excludedRelations, { stage, memory })
45
+ return excludeModelRelations(excludedRelations, { stage, memory }, where)
43
46
  }
44
47
  if (FqnExpr.isWildcard(expr.outgoing)) {
45
48
  // non-sense
@@ -47,7 +50,11 @@ export const OutgoingRelationPredicate: PredicateExecutor<RelationExpr.Outgoing>
47
50
  }
48
51
 
49
52
  const isOutgoing = filterOutgoingConnections(resolveElements(model, expr.outgoing))
50
- const toExclude = memory.connections.filter(isOutgoing)
53
+ const toExclude = pipe(
54
+ memory.connections,
55
+ filter(isOutgoing),
56
+ applyPredicate(where),
57
+ )
51
58
  stage.excludeConnections(toExclude)
52
59
  return stage
53
60
  },
@@ -0,0 +1,18 @@
1
+ import { whereOperatorAsPredicate, type RelationExpr } from '../../../types'
2
+ import type { PredicateExecutor } from '../_types'
3
+ import type { StageExclude, StageInclude } from '../memory'
4
+ import { predicateToPatch } from './utils'
5
+
6
+ // relation matches the condition
7
+ export const WhereRelationPredicate: PredicateExecutor<RelationExpr.Where> = {
8
+ include: ({ expr, model, memory, stage }) => {
9
+ const where = whereOperatorAsPredicate(expr.where.condition)
10
+
11
+ return predicateToPatch('include', { expr: expr.where.expr, model, stage, memory, where }) as StageInclude
12
+ },
13
+ exclude: ({ expr, model, memory, stage }) => {
14
+ const where = whereOperatorAsPredicate(expr.where.condition)
15
+
16
+ return predicateToPatch('exclude', { expr: expr.where.expr, model, stage, memory, where }) as StageExclude
17
+ }
18
+ }
@@ -0,0 +1,214 @@
1
+ import { filter, isArray, map, pick, pipe } from 'remeda'
2
+ import { nonexhaustive } from '../../../errors'
3
+ import { type DeploymentConnectionModel, ElementModel, isDeployedInstance, isDeploymentNode } from '../../../model'
4
+ import type {
5
+ DeploymentElementModel,
6
+ DeploymentRelationEndpoint,
7
+ DeploymentRelationModel,
8
+ } from '../../../model/DeploymentElementModel'
9
+ import { isElementModel } from '../../../model/ElementModel'
10
+ import { isDeploymentElementModel, isNestedElementOfDeployedInstanceModel } from '../../../model/guards'
11
+ import type { RelationshipModel } from '../../../model/RelationModel'
12
+ import type { AnyAux } from '../../../model/types'
13
+ import { type Filterable, type OperatorPredicate, FqnExpr, RelationExpr } from '../../../types'
14
+ import { hasIntersection, intersection } from '../../../utils/set'
15
+ import type { ExcludePredicateCtx, PredicateCtx } from '../_types'
16
+ import type { StageExclude, StageInclude } from '../memory'
17
+ import { DeploymentRefPredicate } from './elements'
18
+ import { DirectRelationPredicate } from './relation-direct'
19
+ import { InOutRelationPredicate } from './relation-in-out'
20
+ import { IncomingRelationPredicate } from './relation-incoming'
21
+ import { OutgoingRelationPredicate } from './relation-outgoing'
22
+ import { WhereRelationPredicate } from './relation-where'
23
+ import { WildcardPredicate } from './wildcard'
24
+
25
+ /**
26
+ * Builds a patch object from an expression
27
+ */
28
+ export function predicateToPatch(
29
+ op: 'include' | 'exclude',
30
+ { expr, where, ...ctx }: PredicateCtx,
31
+ ): StageExclude | StageInclude | undefined {
32
+ switch (true) {
33
+ case FqnExpr.isModelRef(expr):
34
+ // Ignore model refs in deployment view
35
+ return undefined
36
+ case FqnExpr.isDeploymentRef(expr):
37
+ return DeploymentRefPredicate[op]({ ...ctx, expr, where } as any)
38
+ case FqnExpr.isWildcard(expr):
39
+ return WildcardPredicate[op]({ ...ctx, expr, where } as any)
40
+ case RelationExpr.isDirect(expr):
41
+ return DirectRelationPredicate[op]({ ...ctx, expr, where } as any)
42
+ case RelationExpr.isInOut(expr):
43
+ return InOutRelationPredicate[op]({ ...ctx, expr, where } as any)
44
+ case RelationExpr.isOutgoing(expr):
45
+ return OutgoingRelationPredicate[op]({ ...ctx, expr, where } as any)
46
+ case RelationExpr.isIncoming(expr):
47
+ return IncomingRelationPredicate[op]({ ...ctx, expr, where } as any)
48
+ case RelationExpr.isWhere(expr):
49
+ return WhereRelationPredicate[op]({ ...ctx, expr, where } as any)
50
+ default:
51
+ nonexhaustive(expr)
52
+ }
53
+ }
54
+
55
+ export function excludeModelRelations(
56
+ relationsToExclude: ReadonlySet<RelationshipModel<AnyAux>>,
57
+ { stage, memory }: Pick<ExcludePredicateCtx, 'stage' | 'memory'>,
58
+ where: OperatorPredicate<Filterable> | null,
59
+ // Optional filter to scope the connections to exclude
60
+ filterConnections: (c: DeploymentConnectionModel) => boolean = () => true,
61
+ ): StageExclude {
62
+ if (relationsToExclude.size === 0) {
63
+ return stage
64
+ }
65
+ const toExclude = pipe(
66
+ memory.connections,
67
+ filter(c => filterConnections(c)),
68
+ // Find connections that have at least one relation in common with the excluded relations
69
+ filter(c => hasIntersection(c.relations.model, relationsToExclude)),
70
+ map(c =>
71
+ c.update({
72
+ deployment: null,
73
+ model: intersection(c.relations.model, relationsToExclude),
74
+ })
75
+ ),
76
+ applyPredicate(where),
77
+ filter(c => c.nonEmpty()),
78
+ )
79
+ if (toExclude.length === 0) {
80
+ return stage
81
+ }
82
+ return stage.excludeConnections(toExclude)
83
+ }
84
+
85
+ export function matchConnection<M extends AnyAux>(
86
+ c: DeploymentConnectionModel<M>,
87
+ where: OperatorPredicate<Filterable> | null,
88
+ ): boolean {
89
+ return applyPredicate(c, where).nonEmpty()
90
+ }
91
+
92
+ /**
93
+ * Filters relations of the provided connections using the provided predicate.
94
+ * And copy of the connection with the filtered relations will be created.
95
+ * Connections left without relations are removed from resulting collection.
96
+ *
97
+ * @param c The connection to apply the predicate to
98
+ * @param where The predicate
99
+ * @returns A copy of the connection with the filtered relations
100
+ */
101
+ export function applyPredicate<M extends AnyAux>(
102
+ c: readonly DeploymentConnectionModel<M>[],
103
+ where: OperatorPredicate<Filterable> | null,
104
+ ): readonly DeploymentConnectionModel<M>[]
105
+ /**
106
+ * Filters relations of the connection using the provided predicate.
107
+ *
108
+ * @param c The connection to apply the predicate to
109
+ * @param where The predicate
110
+ * @returns A deep copy of the original collection with the filtered relations
111
+ */
112
+ export function applyPredicate<M extends AnyAux>(
113
+ c: DeploymentConnectionModel<M>,
114
+ where: OperatorPredicate<Filterable> | null,
115
+ ): DeploymentConnectionModel<M>
116
+ /**
117
+ * Creates a function that filters relations of the provided connections using the provided predicate.
118
+ * Connections left without relations are removed from resulting collection.
119
+ *
120
+ * @param c The connection to apply the predicate to
121
+ * @param where The predicate
122
+ * @returns A function to create a filtered copy of connections
123
+ */
124
+ export function applyPredicate<M extends AnyAux>(
125
+ where: OperatorPredicate<Filterable> | null,
126
+ ): (data: readonly DeploymentConnectionModel<M>[]) => readonly DeploymentConnectionModel<M>[]
127
+ export function applyPredicate<M extends AnyAux>(
128
+ ...args:
129
+ | [readonly DeploymentConnectionModel<M>[], OperatorPredicate<Filterable> | null]
130
+ | [OperatorPredicate<Filterable> | null]
131
+ | [c: DeploymentConnectionModel<M>, OperatorPredicate<Filterable> | null]
132
+ ):
133
+ | DeploymentConnectionModel<M>
134
+ | readonly DeploymentConnectionModel<M>[]
135
+ | ((data: readonly DeploymentConnectionModel<M>[]) => readonly DeploymentConnectionModel<M>[])
136
+ {
137
+ if (args.length === 1) {
138
+ return x => applyPredicate(x, args[0])
139
+ }
140
+
141
+ const [c, where] = args
142
+ if (where === null) {
143
+ return c
144
+ }
145
+
146
+ if (isArray(c)) {
147
+ return c
148
+ .map(x => applyPredicate(x, where))
149
+ .filter(x => x.nonEmpty())
150
+ }
151
+
152
+ return c.update({
153
+ model: new Set([...c.relations.model.values()].filter(r => where(toFilterableRelation(c)(r)))),
154
+ deployment: new Set([...c.relations.deployment.values()].filter(r => where(toFilterableRelation(c)(r)))),
155
+ })
156
+ }
157
+
158
+ export function matchConnections<M extends AnyAux>(
159
+ connections: readonly DeploymentConnectionModel<M>[],
160
+ where: OperatorPredicate<Filterable> | null,
161
+ ): readonly DeploymentConnectionModel[] {
162
+ if (!where) {
163
+ return connections
164
+ }
165
+
166
+ return pipe(
167
+ connections,
168
+ filter(c => matchConnection(c, where)),
169
+ )
170
+ }
171
+
172
+ /**
173
+ * Builds filterable presentation for relation endpoint. Enriches model properties with deployment properties when needed.
174
+ * @param relationEndpoint Endpoint for which to build presentation
175
+ * @param connectionEndpoint Endpoint of connection which was build from the relation.
176
+ * @returns Presentation of the endpoint for usage in predicates
177
+ */
178
+ function toFilterable<M extends AnyAux>(
179
+ relationEndpoint: ElementModel<M> | DeploymentRelationEndpoint<M>,
180
+ connectionEndpoint: DeploymentRelationEndpoint<M>,
181
+ ): Filterable {
182
+ if (isElementModel(relationEndpoint)) { // Element itself. Extend with tags of the deployed instance (TODO)
183
+ const deployedInstance = isDeploymentElementModel(connectionEndpoint) && isDeployedInstance(connectionEndpoint)
184
+ ? connectionEndpoint
185
+ : null
186
+ return {
187
+ kind: relationEndpoint.kind,
188
+ tags: [...relationEndpoint.tags, ...(deployedInstance?.tags ?? [])],
189
+ }
190
+ }
191
+ if (isNestedElementOfDeployedInstanceModel(relationEndpoint)) { // Nested element. Has no own instance. No need to extend
192
+ return pick(relationEndpoint.element, ['tags', 'kind'])
193
+ }
194
+ if (isDeployedInstance(relationEndpoint)) { // Deployed instance. Extend with tags of the model element
195
+ return {
196
+ kind: relationEndpoint.element.kind,
197
+ tags: [...relationEndpoint.tags, ...relationEndpoint.element.tags],
198
+ }
199
+ }
200
+ if (isDeploymentNode(relationEndpoint)) { // Deployment node. Has no representation in model. No need to extend
201
+ return pick(relationEndpoint, ['tags', 'kind'])
202
+ }
203
+
204
+ nonexhaustive(relationEndpoint)
205
+ }
206
+
207
+ function toFilterableRelation<M extends AnyAux>(c: DeploymentConnectionModel<M>) {
208
+ return (relation: RelationshipModel<M> | DeploymentRelationModel<M>) => ({
209
+ tags: relation.tags,
210
+ kind: relation.kind,
211
+ source: toFilterable(relation.source, c.source),
212
+ target: toFilterable(relation.target, c.target),
213
+ })
214
+ }
@@ -18,10 +18,7 @@ import {
18
18
  type IconUrl,
19
19
  type IncomingExpr as C4IncomingExpr,
20
20
  type InOutExpr as C4InOutExpr,
21
- isElementRef,
22
- isElementWhere,
23
- isRelationExpression,
24
- isRelationWhere,
21
+ type KindEqual,
25
22
  type ModelRelation,
26
23
  type NonEmptyArray,
27
24
  type OutgoingExpr as C4OutgoingExpr,
@@ -30,6 +27,7 @@ import {
30
27
  type RelationshipLineType,
31
28
  type RelationWhereExpr,
32
29
  type Tag,
30
+ type TagEqual,
33
31
  type ViewId,
34
32
  type ViewRule,
35
33
  type ViewRuleGlobalPredicateRef,
@@ -38,8 +36,13 @@ import {
38
36
  type ViewRulePredicate,
39
37
  type ViewRuleStyle,
40
38
  type WhereOperator,
39
+ isElementRef,
40
+ isElementWhere,
41
+ isRelationExpression,
42
+ isRelationWhere,
41
43
  } from '../../../types'
42
44
  import { type DirectRelationExpr as C4RelationExpr } from '../../../types/expression'
45
+ import type { Participant } from '../../../types/operators'
43
46
  import { withReadableEdges } from '../../utils/with-readable-edges'
44
47
  import { computeElementView } from '../compute'
45
48
 
@@ -482,6 +485,16 @@ export function $where(
482
485
  }
483
486
  }
484
487
 
488
+ export function $participant(
489
+ participant: Participant,
490
+ operator: TagEqual<TestTag> | KindEqual<TestTag>,
491
+ ): WhereOperator<TestTag, string> {
492
+ return {
493
+ participant,
494
+ operator,
495
+ }
496
+ }
497
+
485
498
  export function $inout(
486
499
  expr: InOutExpr | C4ElementExpression,
487
500
  ): C4InOutExpr {
@@ -1,8 +1,9 @@
1
1
  import DefaultMap from 'mnemonist/default-map'
2
2
  import { filter, findLast, forEach, map, pipe } from 'remeda'
3
3
  import { invariant, nonexhaustive, nonNullable } from '../../errors'
4
- import { type ElementModel, LikeC4Model } from '../../model'
5
4
  import { ConnectionModel } from '../../model/connection/model/ConnectionModel'
5
+ import { type ElementModel } from '../../model/ElementModel'
6
+ import { LikeC4Model } from '../../model/LikeC4Model'
6
7
  import type { RelationshipModel } from '../../model/RelationModel'
7
8
  import type { AnyAux } from '../../model/types'
8
9
  import type {
@@ -15,7 +16,7 @@ import type {
15
16
  } from '../../types'
16
17
  import { isViewRuleAutoLayout, isViewRuleGroup, isViewRulePredicate, whereOperatorAsPredicate } from '../../types'
17
18
  import * as Expr from '../../types/expression'
18
- import { sortNaturalByFqn, sortParentsFirst } from '../../utils'
19
+ import { sortParentsFirst } from '../../utils'
19
20
  import { applyCustomElementProperties } from '../utils/applyCustomElementProperties'
20
21
  import { applyCustomRelationProperties } from '../utils/applyCustomRelationProperties'
21
22
  import { applyViewRuleStyles } from '../utils/applyViewRuleStyles'