@likec4/core 1.17.1 → 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.
Files changed (184) hide show
  1. package/LICENSE +1 -1
  2. package/dist/builder/index.d.mts +502 -0
  3. package/dist/builder/index.d.ts +502 -0
  4. package/dist/builder/index.js +871 -0
  5. package/dist/compute-view/index.d.mts +22 -0
  6. package/dist/compute-view/index.d.ts +22 -0
  7. package/dist/compute-view/index.js +78 -0
  8. package/dist/index.d.mts +10 -919
  9. package/dist/index.d.ts +10 -919
  10. package/dist/{index.mjs → index.js} +7 -1688
  11. package/dist/model/index.d.mts +254 -0
  12. package/dist/model/index.d.ts +254 -0
  13. package/dist/model/index.js +1 -0
  14. package/dist/shared/chunk-RAAYCPUM.M-JWF7SS.js +21 -0
  15. package/dist/shared/core.BIfgabEw.d.ts +719 -0
  16. package/dist/shared/core.BZ9Msq7w.d.mts +719 -0
  17. package/dist/shared/{core.CoLQkcYL.d.cts → core.C05RDLhi.d.ts} +460 -362
  18. package/dist/shared/{core.CoLQkcYL.d.mts → core.CmYMqgha.d.mts} +460 -362
  19. package/dist/shared/core.D6-fWbM3.d.ts +127 -0
  20. package/dist/shared/core.D74xkKkG.d.mts +148 -0
  21. package/dist/shared/core.D74xkKkG.d.ts +148 -0
  22. package/dist/shared/core.sLaWHBjR.d.mts +127 -0
  23. package/dist/shared/index.-BdzdI2C.js +7443 -0
  24. package/dist/shared/object_hash.CE_oF3I3.js +1222 -0
  25. package/dist/shared/{core.BXTs-8n7.mjs → view.CyZrG8BJ.js} +108 -47
  26. package/dist/types/index.d.mts +71 -1
  27. package/dist/types/index.d.ts +71 -1
  28. package/dist/types/index.js +20 -0
  29. package/dist/utils/index.d.mts +404 -0
  30. package/dist/utils/index.d.ts +404 -0
  31. package/dist/utils/index.js +330 -0
  32. package/package.json +59 -47
  33. package/src/builder/Builder-style1.test-d.ts +190 -0
  34. package/src/builder/Builder-style2.test-d.ts +175 -0
  35. package/src/builder/Builder.deployment.ts +224 -0
  36. package/src/builder/Builder.deploymentModel.ts +385 -0
  37. package/src/builder/Builder.element.ts +19 -19
  38. package/src/builder/Builder.model.ts +67 -42
  39. package/src/builder/Builder.ts +533 -367
  40. package/src/builder/Builder.view-common.ts +234 -0
  41. package/src/builder/Builder.view-deployment.ts +135 -0
  42. package/src/builder/Builder.view-element.ts +189 -0
  43. package/src/builder/Builder.views.ts +109 -33
  44. package/src/builder/Builder.with.ts +475 -0
  45. package/src/builder/__snapshots__/Builder-style1.spec.ts.snap +374 -0
  46. package/src/builder/__snapshots__/Builder-style2.spec.ts.snap +183 -0
  47. package/src/builder/__snapshots__/Builder.spec.ts.snap +374 -0
  48. package/src/builder/_types.ts +113 -18
  49. package/src/builder/index.ts +10 -4
  50. package/src/colors/index.ts +2 -2
  51. package/src/compute-view/compute-view.ts +83 -0
  52. package/src/compute-view/deployment-view/__test__/TestHelper.ts +236 -0
  53. package/src/compute-view/deployment-view/__test__/fixture.ts +198 -0
  54. package/src/compute-view/deployment-view/_types.ts +37 -0
  55. package/src/compute-view/deployment-view/clean-connections.ts +238 -0
  56. package/src/compute-view/deployment-view/compute.ts +125 -0
  57. package/src/compute-view/deployment-view/memory/index.ts +9 -0
  58. package/src/compute-view/deployment-view/memory/memory.ts +97 -0
  59. package/src/compute-view/deployment-view/predicates/elements.ts +144 -0
  60. package/src/compute-view/deployment-view/predicates/relation-direct.ts +237 -0
  61. package/src/compute-view/deployment-view/predicates/relation-in-out.ts +61 -0
  62. package/src/compute-view/deployment-view/predicates/relation-incoming.ts +95 -0
  63. package/src/compute-view/deployment-view/predicates/relation-outgoing.ts +88 -0
  64. package/src/compute-view/deployment-view/predicates/wildcard.ts +29 -0
  65. package/src/compute-view/deployment-view/stages/stage-exclude.ts +5 -0
  66. package/src/compute-view/deployment-view/stages/stage-final.ts +245 -0
  67. package/src/compute-view/deployment-view/stages/stage-include.ts +95 -0
  68. package/src/compute-view/deployment-view/utils.ts +276 -0
  69. package/src/compute-view/dynamic-view/__test__/fixture.ts +58 -0
  70. package/src/compute-view/dynamic-view/compute.ts +324 -0
  71. package/src/compute-view/element-view/__test__/TestHelper.ts +258 -0
  72. package/src/compute-view/element-view/__test__/__snapshots__/legacy.spec.ts.snap +458 -0
  73. package/src/compute-view/element-view/__test__/fixture.ts +675 -0
  74. package/src/compute-view/element-view/_types.ts +48 -0
  75. package/src/compute-view/element-view/clean-connections.ts +113 -0
  76. package/src/compute-view/element-view/compute.ts +315 -0
  77. package/src/compute-view/element-view/memory/NodeGroup.ts +31 -0
  78. package/src/compute-view/element-view/memory/index.ts +10 -0
  79. package/src/compute-view/element-view/memory/memory.ts +177 -0
  80. package/src/compute-view/element-view/memory/stage-exclude.ts +59 -0
  81. package/src/compute-view/element-view/memory/stage-final.ts +129 -0
  82. package/src/compute-view/element-view/memory/stage-include.ts +128 -0
  83. package/src/compute-view/element-view/predicates/_utils.ts +86 -0
  84. package/src/compute-view/element-view/predicates/element-expand.ts +33 -0
  85. package/src/compute-view/element-view/predicates/element-kind-tag.ts +26 -0
  86. package/src/compute-view/element-view/predicates/element-ref.ts +37 -0
  87. package/src/compute-view/element-view/predicates/relation-direct.ts +309 -0
  88. package/src/compute-view/element-view/predicates/relation-in-out.ts +66 -0
  89. package/src/compute-view/element-view/predicates/relation-in.ts +118 -0
  90. package/src/compute-view/element-view/predicates/relation-out.ts +116 -0
  91. package/src/compute-view/element-view/predicates/wildcard.ts +88 -0
  92. package/src/compute-view/element-view/utils.ts +59 -0
  93. package/src/compute-view/index.ts +4 -0
  94. package/src/compute-view/memory/AbstractMemory.ts +45 -0
  95. package/src/compute-view/memory/AbstractStageExclude.ts +216 -0
  96. package/src/compute-view/memory/AbstractStageInclude.ts +200 -0
  97. package/src/compute-view/memory/_types.ts +147 -0
  98. package/src/compute-view/memory/index.ts +5 -0
  99. package/src/compute-view/memory/ops.ts +43 -0
  100. package/src/compute-view/utils/__snapshots__/sortNodes.spec.ts.snap +113 -0
  101. package/src/compute-view/utils/ancestorsOfNode.ts +19 -0
  102. package/src/compute-view/utils/applyCustomElementProperties.ts +66 -0
  103. package/src/compute-view/utils/applyCustomRelationProperties.ts +41 -0
  104. package/src/compute-view/utils/applyViewRuleStyles.ts +53 -0
  105. package/src/compute-view/utils/buildComputedNodes.ts +148 -0
  106. package/src/compute-view/utils/buildElementNotations.ts +63 -0
  107. package/src/compute-view/utils/elementExpressionToPredicate.ts +50 -0
  108. package/src/compute-view/utils/link-nodes-with-edges.ts +54 -0
  109. package/src/compute-view/utils/merge-props-from-relationships.ts +128 -0
  110. package/src/compute-view/utils/relationExpressionToPredicates.ts +55 -0
  111. package/src/compute-view/utils/resolve-extended-views.ts +69 -0
  112. package/src/compute-view/utils/resolve-global-rules.ts +83 -0
  113. package/src/compute-view/utils/topological-sort.ts +256 -0
  114. package/src/compute-view/utils/uniqueTags.test.ts +42 -0
  115. package/src/compute-view/utils/uniqueTags.ts +21 -0
  116. package/src/compute-view/utils/view-hash.ts +27 -0
  117. package/src/compute-view/utils/with-readable-edges.ts +36 -0
  118. package/src/index.ts +22 -12
  119. package/src/model/DeploymentElementModel.ts +599 -0
  120. package/src/model/DeploymentModel.ts +370 -0
  121. package/src/model/ElementModel.ts +221 -0
  122. package/src/model/LikeC4Model.test-d.ts +152 -0
  123. package/src/model/LikeC4Model.ts +336 -457
  124. package/src/model/RelationModel.ts +85 -0
  125. package/src/model/__test__/fixture.ts +149 -338
  126. package/src/model/connection/Connection.ts +72 -0
  127. package/src/model/connection/deployment/DeploymentConnectionModel.ts +178 -0
  128. package/src/model/connection/deployment/find.ts +120 -0
  129. package/src/model/connection/deployment/index.ts +8 -0
  130. package/src/model/connection/index.ts +22 -0
  131. package/src/model/connection/model/ConnectionModel.ts +157 -0
  132. package/src/model/connection/model/find.ts +117 -0
  133. package/src/model/connection/model/index.ts +8 -0
  134. package/src/model/connection/ops.ts +274 -0
  135. package/src/model/guards.ts +9 -0
  136. package/src/model/index.ts +46 -2
  137. package/src/model/types.ts +112 -15
  138. package/src/model/view/EdgeModel.ts +103 -0
  139. package/src/model/view/LikeC4ViewModel.ts +226 -0
  140. package/src/model/view/NodeModel.ts +229 -0
  141. package/src/types/_common.ts +55 -1
  142. package/src/types/deployments.ts +169 -0
  143. package/src/types/element.ts +9 -5
  144. package/src/types/expression-v2.ts +172 -0
  145. package/src/types/expression.ts +17 -11
  146. package/src/types/global.ts +3 -3
  147. package/src/types/index.ts +220 -12
  148. package/src/types/model.ts +75 -12
  149. package/src/types/operators.ts +1 -1
  150. package/src/types/overview-graph.ts +2 -2
  151. package/src/types/relation.ts +18 -14
  152. package/src/types/theme.ts +0 -1
  153. package/src/types/view-notation.ts +2 -2
  154. package/src/types/view.ts +117 -41
  155. package/src/utils/{commonHead.ts → common-head.ts} +2 -0
  156. package/src/utils/const.ts +1 -0
  157. package/src/utils/fqn.ts +203 -56
  158. package/src/utils/getOrCreate.ts +42 -0
  159. package/src/utils/graphlib.ts +10 -0
  160. package/src/utils/index.ts +74 -6
  161. package/src/utils/iterable/_types.ts +5 -0
  162. package/src/utils/iterable/filter.ts +55 -0
  163. package/src/utils/iterable/flat.ts +17 -0
  164. package/src/utils/iterable/index.ts +12 -0
  165. package/src/utils/iterable/map.ts +40 -0
  166. package/src/utils/iterable/reduce.ts +24 -0
  167. package/src/utils/iterable/some.ts +36 -0
  168. package/src/utils/iterable/to.ts +19 -0
  169. package/src/utils/iterable/unique.ts +29 -0
  170. package/src/utils/object-hash.ts +3 -0
  171. package/src/utils/relations.ts +3 -64
  172. package/src/utils/set.ts +78 -0
  173. package/src/utils/string-hash.ts +16 -0
  174. package/dist/index.cjs +0 -5452
  175. package/dist/index.d.cts +0 -1053
  176. package/dist/shared/core.CoLQkcYL.d.ts +0 -734
  177. package/dist/shared/core.DIYZvpVg.cjs +0 -353
  178. package/dist/types/index.cjs +0 -70
  179. package/dist/types/index.d.cts +0 -2
  180. package/dist/types/index.mjs +0 -1
  181. package/src/builder/Builder.test-d.ts +0 -69
  182. package/src/builder/Builder.view.ts +0 -358
  183. package/src/model/LikeC4DiagramModel.ts +0 -338
  184. package/src/model/LikeC4ViewModel.ts +0 -320
@@ -0,0 +1,309 @@
1
+ import { concat, constant, flatMap, hasAtLeast, map, partition, pipe, piped, prop, when } from 'remeda'
2
+ import { invariant } from '../../../errors'
3
+ import { ConnectionModel, findConnectionsBetween, findConnectionsWithin } from '../../../model/connection/model'
4
+ import type { RelationshipModel } from '../../../model/RelationModel'
5
+ import type { AnyAux } from '../../../model/types'
6
+ import * as Expr from '../../../types/expression'
7
+ import { isSameHierarchy } from '../../../utils'
8
+ import { ifilter, iflat, iunique, toArray, toSet } from '../../../utils/iterable'
9
+ import { intersection, union } from '../../../utils/set'
10
+ import type { Elem, PredicateCtx, PredicateExecutor } from '../_types'
11
+ import { NoWhere } from '../utils'
12
+ import { includeDescendantsFromMemory, resolveAndIncludeFromMemory, resolveElements } from './_utils'
13
+
14
+ export const DirectRelationExprPredicate: PredicateExecutor<Expr.DirectRelationExpr> = {
15
+ include: ({ expr: { source, target, isBidirectional = false }, memory, model, stage, where, filterWhere }) => {
16
+ const sourceIsWildcard = Expr.isWildcard(source)
17
+ const targetIsWildcard = Expr.isWildcard(target)
18
+
19
+ const connections = [] as ConnectionModel<AnyAux>[]
20
+ switch (true) {
21
+ // This is a special case, we look for all relationships that satisfy the where clause
22
+ // * -> * ; Empty memory; Where clause
23
+ case sourceIsWildcard && targetIsWildcard && memory.isEmpty() && where !== NoWhere: {
24
+ const connections = pipe(
25
+ model.relationships(),
26
+ ifilter(where),
27
+ toArray(),
28
+ map(r => new ConnectionModel(r.source, r.target, new Set([r]))),
29
+ )
30
+ stage.addConnections(connections)
31
+ return stage
32
+ }
33
+
34
+ // This is a special case, we look for all relationships between elements that satisfy the where clause
35
+ // * -> *; Not empty memory; Where clause
36
+ case sourceIsWildcard && targetIsWildcard && !memory.isEmpty() && where !== NoWhere: {
37
+ connections.push(
38
+ ...findConnectionsWithin(memory.elements),
39
+ )
40
+ break
41
+ }
42
+
43
+ // * -> *
44
+ case sourceIsWildcard && targetIsWildcard: {
45
+ connections.push(
46
+ ...findConnectionsWithin(model.roots()),
47
+ )
48
+ break
49
+ }
50
+
51
+ // This is a special case, we look for all relationships that satisfy the where clause
52
+ // element -> *; Where clause
53
+ case !sourceIsWildcard && targetIsWildcard && where !== NoWhere: {
54
+ const sources = resolveElements(model, source)
55
+ const connections = pipe(
56
+ sources,
57
+ flatMap(source =>
58
+ pipe(
59
+ source,
60
+ when(constant(isBidirectional === true), {
61
+ onTrue: s => union(s.allIncoming, s.allOutgoing),
62
+ onFalse: s => s.allOutgoing,
63
+ }),
64
+ ifilter(where),
65
+ iunique(),
66
+ toArray(),
67
+ partition(r => r.source === source),
68
+ ([outgoing, incoming]) =>
69
+ concat(
70
+ pipe(
71
+ outgoing,
72
+ map(outgoing =>
73
+ new ConnectionModel(
74
+ source,
75
+ outgoing.target,
76
+ new Set([outgoing]),
77
+ )
78
+ ),
79
+ ),
80
+ pipe(
81
+ incoming,
82
+ map(incoming =>
83
+ new ConnectionModel(
84
+ incoming.source,
85
+ source,
86
+ new Set([incoming]),
87
+ )
88
+ ),
89
+ ),
90
+ ),
91
+ )
92
+ ),
93
+ )
94
+ stage.addConnections(connections)
95
+ return stage
96
+ }
97
+
98
+ // element -> *
99
+ case !sourceIsWildcard && targetIsWildcard: {
100
+ const [sources, targets] = resolveWildcard(source, { memory, model })
101
+ const dir = isBidirectional ? 'both' : 'directed'
102
+ for (const source of sources) {
103
+ connections.push(
104
+ ...findConnectionsBetween(source, targets, dir),
105
+ )
106
+ }
107
+ break
108
+ }
109
+
110
+ // This is a special case, we look for all relationships that satisfy the where clause
111
+ // * -> element; Where clause
112
+ case sourceIsWildcard && !targetIsWildcard && where !== NoWhere: {
113
+ const targets = resolveElements(model, target)
114
+ const connections = pipe(
115
+ targets,
116
+ flatMap(target =>
117
+ pipe(
118
+ target,
119
+ when(constant(isBidirectional === true), {
120
+ onTrue: s => union(s.allIncoming, s.allOutgoing),
121
+ onFalse: s => s.allIncoming,
122
+ }),
123
+ ifilter(where),
124
+ toArray(),
125
+ partition(r => r.target === target),
126
+ ([incoming, outgoing]) =>
127
+ concat(
128
+ pipe(
129
+ outgoing,
130
+ map(outgoing =>
131
+ new ConnectionModel(
132
+ target,
133
+ outgoing.target,
134
+ new Set([outgoing]),
135
+ )
136
+ ),
137
+ ),
138
+ pipe(
139
+ incoming,
140
+ map(incoming =>
141
+ new ConnectionModel(
142
+ incoming.source,
143
+ target,
144
+ new Set([incoming]),
145
+ )
146
+ ),
147
+ ),
148
+ ),
149
+ )
150
+ ),
151
+ )
152
+ stage.addConnections(connections)
153
+ return stage
154
+ }
155
+
156
+ // * -> element
157
+ case sourceIsWildcard && !targetIsWildcard: {
158
+ const [targets, sources] = resolveWildcard(target, { memory, model })
159
+ const dir = isBidirectional ? 'both' : 'directed'
160
+ for (const source of sources) {
161
+ connections.push(
162
+ ...findConnectionsBetween(source, targets, dir),
163
+ )
164
+ }
165
+ break
166
+ }
167
+
168
+ default: {
169
+ invariant(!Expr.isWildcard(source), 'Inference failed - source must be not a wildcard')
170
+ invariant(!Expr.isWildcard(target), 'Inference failed - target must be not a wildcard')
171
+ const sources = resolveAndIncludeFromMemory(source, { memory, model })
172
+ const targets = resolveAndIncludeFromMemory(target, { memory, model })
173
+ const dir = isBidirectional ? 'both' : 'directed'
174
+ for (const source of sources) {
175
+ connections.push(
176
+ ...findConnectionsBetween(source, targets, dir),
177
+ )
178
+ }
179
+ }
180
+ }
181
+
182
+ stage.addConnections(
183
+ filterWhere(connections),
184
+ )
185
+
186
+ return stage
187
+ },
188
+ exclude: ({ expr: { source, target, isBidirectional }, model, memory, stage, where }) => {
189
+ const sourceIsWildcard = Expr.isWildcard(source)
190
+ const targetIsWildcard = Expr.isWildcard(target)
191
+
192
+ let relations: Set<RelationshipModel<AnyAux>>
193
+
194
+ switch (true) {
195
+ // * -> *
196
+ case sourceIsWildcard && targetIsWildcard: {
197
+ relations = pipe(
198
+ memory.connections,
199
+ flatMap(piped(
200
+ prop('relations'),
201
+ ifilter(where),
202
+ toArray(),
203
+ )),
204
+ toSet(),
205
+ )
206
+ break
207
+ }
208
+
209
+ // element -> *
210
+ case !sourceIsWildcard && targetIsWildcard: {
211
+ const sources = resolveElements(model, source)
212
+ relations = pipe(
213
+ sources,
214
+ flatMap(source =>
215
+ pipe(
216
+ source,
217
+ when(constant(isBidirectional === true), {
218
+ onTrue: s => union(s.allIncoming, s.allOutgoing),
219
+ onFalse: s => s.allOutgoing,
220
+ }),
221
+ ifilter(where),
222
+ toArray(),
223
+ )
224
+ ),
225
+ toSet(),
226
+ )
227
+ break
228
+ }
229
+
230
+ // * -> element
231
+ case sourceIsWildcard && !targetIsWildcard: {
232
+ const targets = resolveElements(model, target)
233
+ relations = pipe(
234
+ targets,
235
+ flatMap(target =>
236
+ pipe(
237
+ target,
238
+ when(constant(isBidirectional === true), {
239
+ onTrue: s => union(s.allIncoming, s.allOutgoing),
240
+ onFalse: s => s.allIncoming,
241
+ }),
242
+ ifilter(where),
243
+ toArray(),
244
+ )
245
+ ),
246
+ toSet(),
247
+ )
248
+ break
249
+ }
250
+
251
+ default: {
252
+ invariant(!Expr.isWildcard(source), 'Inferrence failed - source must be not a wildcard')
253
+ invariant(!Expr.isWildcard(target), 'Inferrence failed - target must be not a wildcard')
254
+ const sources = resolveElements(model, source)
255
+ const targets = resolveElements(model, target)
256
+
257
+ let accum = new Set<RelationshipModel<AnyAux>>()
258
+ for (const source of sources) {
259
+ for (const target of targets) {
260
+ if (isSameHierarchy(source, target)) {
261
+ continue
262
+ }
263
+ accum = union(
264
+ accum,
265
+ intersection(source.allOutgoing, target.allIncoming),
266
+ isBidirectional ? intersection(target.allOutgoing, source.allIncoming) : new Set(),
267
+ )
268
+ }
269
+ }
270
+
271
+ relations = toSet(ifilter(accum, where))
272
+ }
273
+ }
274
+
275
+ stage.excludeRelations(relations)
276
+ return stage
277
+ },
278
+ }
279
+
280
+ /**
281
+ * Resolve elements for both source and target, when one of them is a wildcard
282
+ */
283
+ function resolveWildcard(
284
+ nonWildcard: Expr.NonWilcard,
285
+ { memory, model }: Pick<PredicateCtx, 'model' | 'memory'>,
286
+ ): [elements: Elem[], wildcard: Elem[]] {
287
+ let sources = resolveElements(model, nonWildcard)
288
+ if (!hasAtLeast(sources, 1)) {
289
+ return [[], []]
290
+ }
291
+
292
+ if (Expr.isExpandedElementExpr(nonWildcard) || Expr.isElementRef(nonWildcard)) {
293
+ const parent = model.element(nonWildcard.element ?? nonWildcard.expanded)
294
+ const targets = toArray(parent.ascendingSiblings())
295
+ return [
296
+ includeDescendantsFromMemory(sources, memory),
297
+ includeDescendantsFromMemory(targets, memory),
298
+ ]
299
+ }
300
+ const targets = pipe(
301
+ sources,
302
+ map(el => el.ascendingSiblings()),
303
+ iflat(),
304
+ iunique(),
305
+ toArray(),
306
+ all => includeDescendantsFromMemory(all, memory),
307
+ )
308
+ return [sources, targets]
309
+ }
@@ -0,0 +1,66 @@
1
+ import { unique } from 'remeda'
2
+ import { type ConnectionModel, findConnectionsBetween } from '../../../model/connection/model'
3
+ import type { RelationshipModel } from '../../../model/RelationModel'
4
+ import type { AnyAux } from '../../../model/types'
5
+ import * as Expr from '../../../types/expression'
6
+ import { toArray } from '../../../utils/iterable'
7
+ import { toSet } from '../../../utils/iterable/to'
8
+ import type { PredicateExecutor } from '../_types'
9
+ import { resolveAndIncludeFromMemory, resolveElements } from './_utils'
10
+
11
+ export const InOutRelationPredicate: PredicateExecutor<Expr.InOutExpr> = {
12
+ include: ({ expr: { inout }, scope, model, memory, stage, filterWhere }) => {
13
+ const connections = [] as ConnectionModel<AnyAux>[]
14
+ if (Expr.isWildcard(inout)) {
15
+ if (!scope) {
16
+ return
17
+ }
18
+ connections.push(
19
+ ...findConnectionsBetween(
20
+ scope,
21
+ scope.ascendingSiblings(),
22
+ ),
23
+ )
24
+ } else {
25
+ const elements = resolveAndIncludeFromMemory(inout, { memory, model })
26
+ let visibleElements = [...memory.elements]
27
+ if (visibleElements.length === 0) {
28
+ visibleElements = unique(
29
+ elements.flatMap(el => toArray(el.ascendingSiblings())),
30
+ )
31
+ }
32
+ for (const el of elements) {
33
+ connections.push(
34
+ ...findConnectionsBetween(
35
+ el,
36
+ visibleElements,
37
+ ),
38
+ )
39
+ }
40
+ }
41
+
42
+ stage.addConnections(
43
+ filterWhere(connections),
44
+ )
45
+
46
+ return stage
47
+ },
48
+ exclude: ({ expr: { inout }, model, scope, stage, where }) => {
49
+ const excluded = [] as RelationshipModel[]
50
+ if (Expr.isWildcard(inout)) {
51
+ if (!scope) {
52
+ return
53
+ }
54
+ excluded.push(...scope.allOutgoing)
55
+ excluded.push(...scope.allIncoming)
56
+ } else {
57
+ const elements = resolveElements(model, inout)
58
+ excluded.push(
59
+ ...elements.flatMap(e => [...e.allOutgoing, ...e.allIncoming]),
60
+ )
61
+ }
62
+ stage.excludeRelations(toSet(excluded.filter(where)))
63
+
64
+ return stage
65
+ },
66
+ }
@@ -0,0 +1,118 @@
1
+ import { anyPass, unique } from 'remeda'
2
+ import { nonexhaustive } from '../../../errors'
3
+ import type { LikeC4Model } from '../../../model'
4
+ import {
5
+ Connection,
6
+ type ConnectionModel,
7
+ findConnection,
8
+ findConnectionsBetween
9
+ } from '../../../model/connection/model'
10
+ import type { RelationshipModel } from '../../../model/RelationModel'
11
+ import type { AnyAux } from '../../../model/types'
12
+ import * as Expr from '../../../types/expression'
13
+ import { elementExprToPredicate } from '../../utils/elementExpressionToPredicate'
14
+ import type { ConnectionWhere, PredicateExecutor } from '../_types'
15
+ import { resolveAndIncludeFromMemory, resolveElements } from './_utils'
16
+
17
+ export const IncomingExprPredicate: PredicateExecutor<Expr.IncomingExpr> = {
18
+ include: ({ expr, scope, model, memory, stage, filterWhere }) => {
19
+ const target = expr.incoming
20
+ const connections = [] as ConnectionModel<AnyAux>[]
21
+ if (Expr.isWildcard(target)) {
22
+ if (!scope) {
23
+ return
24
+ }
25
+ for (const sibling of scope.ascendingSiblings()) {
26
+ connections.push(
27
+ ...findConnection(
28
+ sibling,
29
+ scope,
30
+ 'directed'
31
+ )
32
+ )
33
+ }
34
+ } else {
35
+ const targets = resolveAndIncludeFromMemory(target, { memory, model })
36
+ const visibleElements = [...memory.elements]
37
+ if (visibleElements.length === 0) {
38
+ visibleElements.push(
39
+ ...unique(
40
+ targets.flatMap(el => [...el.ascendingSiblings()])
41
+ )
42
+ )
43
+ }
44
+ const ensureIncoming = incomingConnectionPredicate(model, target)
45
+ for (const visible of visibleElements) {
46
+ connections.push(
47
+ ...findConnectionsBetween(
48
+ visible,
49
+ targets,
50
+ 'directed'
51
+ ).filter(ensureIncoming)
52
+ )
53
+ }
54
+ }
55
+
56
+ stage.addConnections(
57
+ filterWhere(connections)
58
+ )
59
+
60
+ return stage
61
+ },
62
+ exclude: ({ expr: { incoming }, model, scope, stage, where }) => {
63
+ const excluded = [] as RelationshipModel[]
64
+ if (Expr.isWildcard(incoming)) {
65
+ if (!scope) {
66
+ return
67
+ }
68
+ excluded.push(...scope.allIncoming)
69
+ } else {
70
+ const elements = resolveElements(model, incoming)
71
+ excluded.push(
72
+ ...elements.flatMap(e => [...e.allIncoming])
73
+ )
74
+ }
75
+ stage.excludeRelations(new Set(excluded.filter(where)))
76
+
77
+ return stage
78
+ }
79
+ }
80
+
81
+ export function incomingConnectionPredicate(
82
+ model: LikeC4Model,
83
+ expr: Exclude<Expr.ElementExpression, Expr.WildcardExpr>
84
+ ): ConnectionWhere {
85
+ switch (true) {
86
+ case Expr.isElementKindExpr(expr):
87
+ case Expr.isElementTagExpr(expr): {
88
+ const isElement = elementExprToPredicate(expr)
89
+ return (connection) => isElement(connection.target)
90
+ }
91
+ case Expr.isElementRef(expr) && expr.isChildren: {
92
+ return anyPass(
93
+ [...model.children(expr.element)].map(
94
+ el => Connection.isIncoming(el.id)
95
+ )
96
+ )
97
+ }
98
+ case Expr.isElementRef(expr) && expr.isDescendants: {
99
+ return anyPass([
100
+ Connection.isInside(expr.element),
101
+ ...[...model.children(expr.element)].map(
102
+ el => Connection.isIncoming(el.id)
103
+ )
104
+ ])
105
+ }
106
+ case Expr.isExpandedElementExpr(expr): {
107
+ return anyPass([
108
+ Connection.isIncoming(expr.expanded),
109
+ Connection.isInside(expr.expanded)
110
+ ])
111
+ }
112
+ case Expr.isElementRef(expr): {
113
+ return Connection.isIncoming(expr.element)
114
+ }
115
+ default:
116
+ nonexhaustive(expr)
117
+ }
118
+ }
@@ -0,0 +1,116 @@
1
+ import { anyPass, unique } from 'remeda'
2
+ import { nonexhaustive } from '../../../errors'
3
+ import type { LikeC4Model } from '../../../model'
4
+ import { Connection, type ConnectionModel, findConnectionsBetween } from '../../../model/connection/model'
5
+ import type { RelationshipModel } from '../../../model/RelationModel'
6
+ import type { AnyAux } from '../../../model/types'
7
+ import * as Expr from '../../../types/expression'
8
+ import { toSet } from '../../../utils/iterable/to'
9
+ import { elementExprToPredicate } from '../../utils/elementExpressionToPredicate'
10
+ import type { ConnectionWhere, PredicateExecutor } from '../_types'
11
+ import { resolveAndIncludeFromMemory, resolveElements } from './_utils'
12
+
13
+ export const OutgoingExprPredicate: PredicateExecutor<Expr.OutgoingExpr> = {
14
+ include: ({ expr, scope, model, memory, stage, filterWhere }) => {
15
+ const target = expr.outgoing
16
+ const connections = [] as ConnectionModel<AnyAux>[]
17
+ if (Expr.isWildcard(target)) {
18
+ if (!scope) {
19
+ return
20
+ }
21
+
22
+ connections.push(
23
+ ...findConnectionsBetween(
24
+ scope,
25
+ scope.ascendingSiblings(),
26
+ 'directed'
27
+ )
28
+ )
29
+ } else {
30
+ const elements = resolveAndIncludeFromMemory(target, { memory, model })
31
+ const visibleElements = [...memory.elements]
32
+ if (visibleElements.length === 0) {
33
+ visibleElements.push(
34
+ ...unique(
35
+ elements.flatMap(el => [...el.ascendingSiblings()])
36
+ )
37
+ )
38
+ }
39
+ const ensureOutgoing = outgoingConnectionPredicate(model, target)
40
+
41
+ for (const source of elements) {
42
+ connections.push(
43
+ ...findConnectionsBetween(
44
+ source,
45
+ visibleElements,
46
+ 'directed'
47
+ ).filter(ensureOutgoing)
48
+ )
49
+ }
50
+ }
51
+
52
+ stage.addConnections(
53
+ filterWhere(connections)
54
+ )
55
+
56
+ return stage
57
+ },
58
+ exclude: ({ expr: { outgoing }, model, scope, stage, where }) => {
59
+ const excluded = [] as RelationshipModel[]
60
+ if (Expr.isWildcard(outgoing)) {
61
+ if (!scope) {
62
+ return
63
+ }
64
+ excluded.push(...scope.allOutgoing)
65
+ } else {
66
+ const elements = resolveElements(model, outgoing)
67
+ excluded.push(
68
+ ...elements.flatMap(e => [...e.allOutgoing])
69
+ )
70
+ }
71
+ stage.excludeRelations(
72
+ toSet(excluded.filter(where))
73
+ )
74
+
75
+ return stage
76
+ }
77
+ }
78
+
79
+ export function outgoingConnectionPredicate(
80
+ model: LikeC4Model,
81
+ expr: Exclude<Expr.ElementExpression, Expr.WildcardExpr>
82
+ ): ConnectionWhere {
83
+ switch (true) {
84
+ case Expr.isElementKindExpr(expr):
85
+ case Expr.isElementTagExpr(expr): {
86
+ const isElement = elementExprToPredicate(expr)
87
+ return (connection) => isElement(connection.source)
88
+ }
89
+ case Expr.isElementRef(expr) && expr.isChildren: {
90
+ return anyPass(
91
+ [...model.children(expr.element)].map(
92
+ el => Connection.isOutgoing(el.id)
93
+ )
94
+ )
95
+ }
96
+ case Expr.isElementRef(expr) && expr.isDescendants: {
97
+ return anyPass([
98
+ Connection.isInside(expr.element),
99
+ ...[...model.children(expr.element)].map(
100
+ el => Connection.isOutgoing(el.id)
101
+ )
102
+ ])
103
+ }
104
+ case Expr.isExpandedElementExpr(expr): {
105
+ return anyPass([
106
+ Connection.isOutgoing(expr.expanded),
107
+ Connection.isInside(expr.expanded)
108
+ ])
109
+ }
110
+ case Expr.isElementRef(expr): {
111
+ return Connection.isOutgoing(expr.element)
112
+ }
113
+ default:
114
+ nonexhaustive(expr)
115
+ }
116
+ }
@@ -0,0 +1,88 @@
1
+ import { filter } from 'remeda'
2
+ import { findConnectionsBetween, findConnectionsWithin } from '../../../model/connection/model'
3
+ import type { WildcardExpr } from '../../../types/expression'
4
+ import { ifilter, toArray } from '../../../utils/iterable'
5
+ import { toSet } from '../../../utils/iterable/to'
6
+ import { type PredicateExecutor, Memory } from '../_types'
7
+ import { NoWhere } from '../utils'
8
+
9
+ export const WildcardPredicate: PredicateExecutor<WildcardExpr> = {
10
+ include: ({ scope, model, stage, memory, where }) => {
11
+ if (!scope) {
12
+ const rootElements = [...model.roots()].filter(where)
13
+ if (rootElements.length === 0) {
14
+ return
15
+ }
16
+ stage.addExplicit(rootElements)
17
+ stage.addConnections(findConnectionsWithin(rootElements))
18
+
19
+ stage.connectWithExisting(rootElements)
20
+
21
+ return stage
22
+ }
23
+ const root = where(scope) ? scope : null
24
+
25
+ const children = toArray(ifilter(scope.children(), where))
26
+ const hasChildren = children.length > 0
27
+ if (!hasChildren) {
28
+ if (!root) {
29
+ return stage
30
+ } else {
31
+ // Any edges with siblings?
32
+ const edgesWithSiblings = findConnectionsBetween(root, root.siblings())
33
+ if (edgesWithSiblings.length === 0) {
34
+ // If no edges with siblings, i.e. root is orphan
35
+ // Lets add parent for better view
36
+ const parent = root.parent
37
+ if (parent && where(parent)) {
38
+ stage.addExplicit(parent)
39
+ }
40
+ }
41
+ children.push(root)
42
+ }
43
+ }
44
+
45
+ if (root) {
46
+ stage.addExplicit(root)
47
+ }
48
+
49
+ const neighbours = toSet([
50
+ ...memory.elements,
51
+ ...scope.descendingSiblings(),
52
+ ])
53
+
54
+ // Add incoming connections
55
+ for (const neighbour of neighbours) {
56
+ stage.addConnections(findConnectionsBetween(neighbour, children, 'directed'))
57
+ }
58
+
59
+ // connection between children
60
+ if (hasChildren) {
61
+ stage.addConnections(findConnectionsWithin(children))
62
+ stage.addExplicit(children)
63
+ }
64
+
65
+ // Add outgoing connections
66
+ for (const child of children) {
67
+ stage.addConnections(findConnectionsBetween(child, neighbours, 'directed'))
68
+ }
69
+
70
+ return stage
71
+ },
72
+ exclude: ({ scope, memory, stage, where }) => {
73
+ if (where !== NoWhere) {
74
+ stage.exclude(
75
+ filter(
76
+ [...memory.elements],
77
+ where,
78
+ ),
79
+ )
80
+ return stage
81
+ }
82
+ if (scope) {
83
+ stage.exclude([scope, ...scope.descendants()])
84
+ return stage
85
+ }
86
+ return Memory.empty(memory.scope).stageExclude(stage.expression)
87
+ },
88
+ }