@likec4/core 1.18.0 → 1.19.1

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 (177) 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 +9 -78
  6. package/dist/compute-view/index.d.ts +9 -78
  7. package/dist/compute-view/index.js +78 -0
  8. package/dist/index.d.mts +9 -730
  9. package/dist/index.d.ts +9 -730
  10. package/dist/{index.mjs → index.js} +7 -784
  11. package/dist/model/index.d.mts +218 -606
  12. package/dist/model/index.d.ts +218 -606
  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.DMYY7C8s.d.mts → core.C05RDLhi.d.ts} +179 -230
  18. package/dist/shared/{core.DMYY7C8s.d.ts → core.CmYMqgha.d.mts} +179 -230
  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.DafVOuVd.js +7443 -0
  24. package/dist/shared/object_hash.CE_oF3I3.js +1222 -0
  25. package/dist/shared/view.CyZrG8BJ.js +354 -0
  26. package/dist/types/index.d.mts +4 -2
  27. package/dist/types/index.d.ts +4 -2
  28. package/dist/types/index.js +20 -0
  29. package/dist/utils/index.d.mts +419 -0
  30. package/dist/utils/index.d.ts +419 -0
  31. package/dist/utils/index.js +347 -0
  32. package/package.json +47 -33
  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 +25 -20
  36. package/src/builder/Builder.deploymentModel.ts +80 -55
  37. package/src/builder/Builder.element.ts +2 -0
  38. package/src/builder/Builder.model.ts +57 -43
  39. package/src/builder/Builder.ts +412 -431
  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 +28 -5
  49. package/src/builder/index.ts +10 -4
  50. package/src/colors/index.ts +2 -2
  51. package/src/compute-view/compute-view.ts +45 -54
  52. package/src/compute-view/deployment-view/__test__/TestHelper.ts +236 -0
  53. package/src/compute-view/deployment-view/__test__/fixture.ts +50 -62
  54. package/src/compute-view/deployment-view/_types.ts +26 -18
  55. package/src/compute-view/deployment-view/clean-connections.ts +173 -164
  56. package/src/compute-view/deployment-view/compute.ts +51 -37
  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 +183 -76
  61. package/src/compute-view/deployment-view/predicates/relation-in-out.ts +38 -20
  62. package/src/compute-view/deployment-view/predicates/relation-incoming.ts +72 -23
  63. package/src/compute-view/deployment-view/predicates/relation-outgoing.ts +61 -22
  64. package/src/compute-view/deployment-view/predicates/wildcard.ts +15 -15
  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 +95 -31
  69. package/src/compute-view/dynamic-view/__test__/fixture.ts +4 -4
  70. package/src/compute-view/dynamic-view/compute.ts +59 -45
  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 +64 -62
  73. package/src/compute-view/element-view/__test__/fixture.ts +147 -117
  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 +264 -683
  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 +3 -4
  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/buildComputedNodes.ts +4 -4
  101. package/src/compute-view/utils/elementExpressionToPredicate.ts +1 -3
  102. package/src/compute-view/utils/link-nodes-with-edges.ts +3 -7
  103. package/src/compute-view/utils/{derive-edge-props-from-relationships.ts → merge-props-from-relationships.ts} +54 -23
  104. package/src/compute-view/utils/relationExpressionToPredicates.ts +2 -2
  105. package/src/compute-view/utils/resolve-extended-views.ts +44 -40
  106. package/src/compute-view/utils/resolve-global-rules.ts +6 -6
  107. package/src/compute-view/utils/topological-sort.ts +167 -71
  108. package/src/compute-view/utils/with-readable-edges.ts +14 -9
  109. package/src/index.ts +22 -16
  110. package/src/model/DeploymentElementModel.ts +150 -85
  111. package/src/model/DeploymentModel.ts +27 -21
  112. package/src/model/ElementModel.ts +51 -25
  113. package/src/model/LikeC4Model.test-d.ts +4 -3
  114. package/src/model/LikeC4Model.ts +98 -42
  115. package/src/model/RelationModel.ts +5 -4
  116. package/src/model/__test__/fixture.ts +5 -9
  117. package/src/model/connection/Connection.ts +72 -0
  118. package/src/model/connection/deployment/DeploymentConnectionModel.ts +178 -0
  119. package/src/model/connection/{deployment.ts → deployment/find.ts} +14 -18
  120. package/src/model/connection/deployment/index.ts +8 -0
  121. package/src/model/connection/index.ts +22 -0
  122. package/src/model/connection/model/ConnectionModel.ts +157 -0
  123. package/src/model/connection/{model.ts → model/find.ts} +43 -49
  124. package/src/model/connection/model/index.ts +8 -0
  125. package/src/model/connection/ops.ts +274 -0
  126. package/src/model/guards.ts +9 -0
  127. package/src/model/index.ts +45 -3
  128. package/src/model/types.ts +35 -18
  129. package/src/model/view/EdgeModel.ts +6 -6
  130. package/src/model/view/LikeC4ViewModel.ts +14 -8
  131. package/src/model/view/NodeModel.ts +6 -5
  132. package/src/types/_common.ts +53 -1
  133. package/src/types/deployments.ts +88 -156
  134. package/src/types/element.ts +2 -2
  135. package/src/types/expression-v2.ts +172 -0
  136. package/src/types/expression.ts +9 -5
  137. package/src/types/global.ts +3 -3
  138. package/src/types/index.ts +220 -13
  139. package/src/types/model.ts +28 -24
  140. package/src/types/operators.ts +1 -1
  141. package/src/types/relation.ts +14 -10
  142. package/src/types/theme.ts +0 -1
  143. package/src/types/view.ts +20 -18
  144. package/src/utils/const.ts +1 -0
  145. package/src/utils/fqn.ts +140 -49
  146. package/src/utils/getOrCreate.ts +1 -1
  147. package/src/utils/index.ts +75 -9
  148. package/src/utils/iterable/_types.ts +5 -0
  149. package/src/utils/iterable/filter.ts +55 -0
  150. package/src/utils/iterable/find.ts +36 -0
  151. package/src/utils/iterable/flat.ts +17 -0
  152. package/src/utils/iterable/index.ts +13 -0
  153. package/src/utils/iterable/map.ts +40 -0
  154. package/src/utils/iterable/reduce.ts +24 -0
  155. package/src/utils/iterable/some.ts +36 -0
  156. package/src/utils/iterable/to.ts +19 -0
  157. package/src/utils/iterable/unique.ts +29 -0
  158. package/src/utils/relations.ts +3 -64
  159. package/src/utils/set.ts +11 -9
  160. package/src/utils/string-hash.ts +1 -1
  161. package/dist/compute-view/index.mjs +0 -79
  162. package/dist/model/index.mjs +0 -2
  163. package/dist/shared/core.BBgiAAv1.mjs +0 -144
  164. package/dist/shared/core.BPyJvuMU.mjs +0 -2420
  165. package/dist/shared/core.BQ4AbfvB.mjs +0 -9228
  166. package/dist/shared/core.k12s_keD.mjs +0 -203
  167. package/dist/types/index.mjs +0 -21
  168. package/src/builder/Builder.test-d.ts +0 -124
  169. package/src/builder/Builder.view.ts +0 -543
  170. package/src/compute-view/LikeC4ModelGraph.ts +0 -330
  171. package/src/compute-view/deployment-view/Memory.ts +0 -108
  172. package/src/compute-view/deployment-view/Stage.ts +0 -145
  173. package/src/compute-view/deployment-view/predicates/deployment-ref.ts +0 -146
  174. package/src/compute-view/element-view/predicates.ts +0 -517
  175. package/src/compute-view/utils/sortNodes.ts +0 -101
  176. package/src/model/connection/ConnectionModel.ts +0 -94
  177. package/src/model/connection/DeploymentConnectionModel.ts +0 -99
@@ -3,44 +3,50 @@ import type { SetRequired } from 'type-fest'
3
3
  import { nonNullable } from '../errors'
4
4
  import {
5
5
  type ComputedDeploymentView,
6
- DefaultElementShape,
7
- DefaultThemeColor,
8
6
  type DeployedInstance,
7
+ type DeploymentElementStyle,
9
8
  type DeploymentNode,
10
9
  type DeploymentNodeKind,
11
10
  type DeploymentRelation,
12
11
  type ElementShape as C4ElementShape,
12
+ type IteratorLike,
13
13
  type Link,
14
- type PhysicalElementStyle,
15
14
  type Tag,
16
15
  type Tag as C4Tag,
17
- type ThemeColor
16
+ type ThemeColor,
17
+ DefaultElementShape,
18
+ DefaultThemeColor,
18
19
  } from '../types'
19
- import { commonAncestor, nameFromFqn } from '../utils'
20
+ import { commonAncestor, hierarchyLevel } from '../utils'
20
21
  import { difference, intersection, union } from '../utils/set'
21
22
  import type { LikeC4DeploymentModel } from './DeploymentModel'
22
23
  import type { ElementModel } from './ElementModel'
23
24
  import type { RelationshipModel, RelationshipsIterator } from './RelationModel'
24
- import type { AnyAux, IncomingFilter, IteratorLike, OutgoingFilter } from './types'
25
+ import type { AnyAux, IncomingFilter, OutgoingFilter } from './types'
25
26
  import type { LikeC4ViewModel } from './view/LikeC4ViewModel'
26
27
 
27
- export type DeploymentElementsIterator<M extends AnyAux> = IteratorLike<DeploymentElementModel<M>>
28
+ export type DeploymentElementsIterator<M extends AnyAux> = IteratorLike<
29
+ DeploymentNodeModel<M> | DeployedInstanceModel<M>
30
+ >
28
31
  export type DeployedInstancesIterator<M extends AnyAux> = IteratorLike<DeployedInstanceModel<M>>
29
32
  export type DeploymentNodesIterator<M extends AnyAux> = IteratorLike<DeploymentNodeModel<M>>
30
33
 
31
- export abstract class DeploymentElementModel<M extends AnyAux = AnyAux> {
34
+ export type DeploymentElementModel<M extends AnyAux = AnyAux> = DeploymentNodeModel<M> | DeployedInstanceModel<M>
35
+
36
+ abstract class AbstractDeploymentElementModel<M extends AnyAux = AnyAux> {
37
+ abstract readonly id: M['DeploymentFqn']
38
+ abstract readonly parent: DeploymentNodeModel<M> | null
39
+ abstract readonly title: string
40
+ abstract readonly hierarchyLevel: number
41
+
32
42
  abstract readonly $model: LikeC4DeploymentModel<M>
33
43
  abstract readonly $node: DeploymentNode | DeployedInstance
34
44
 
35
- get id(): M['DeploymentFqn'] {
36
- return this.$node.id
37
- }
38
-
39
- get style(): SetRequired<PhysicalElementStyle, 'shape' | 'color'> {
45
+ get style(): SetRequired<DeploymentElementStyle, 'shape' | 'color'> {
40
46
  return {
41
47
  shape: DefaultElementShape,
42
48
  color: DefaultThemeColor,
43
- ...this.$node.style
49
+ ...this.$node.style,
44
50
  }
45
51
  }
46
52
 
@@ -56,10 +62,6 @@ export abstract class DeploymentElementModel<M extends AnyAux = AnyAux> {
56
62
  return this.$node.tags ?? []
57
63
  }
58
64
 
59
- get title(): string {
60
- return this.$node.title ?? nameFromFqn(this.id)
61
- }
62
-
63
65
  get description(): string | null {
64
66
  return this.$node.description ?? null
65
67
  }
@@ -68,10 +70,6 @@ export abstract class DeploymentElementModel<M extends AnyAux = AnyAux> {
68
70
  return this.$node.technology ?? null
69
71
  }
70
72
 
71
- get parent(): DeploymentNodeModel<M> | null {
72
- return this.$model.parent(this)
73
- }
74
-
75
73
  get links(): ReadonlyArray<Link> {
76
74
  return this.$node.links ?? []
77
75
  }
@@ -118,6 +116,18 @@ export abstract class DeploymentElementModel<M extends AnyAux = AnyAux> {
118
116
  return
119
117
  }
120
118
 
119
+ /**
120
+ * Resolve siblings of the element and its ancestors
121
+ * (from root to closest)
122
+ */
123
+ public *descendingSiblings(): DeploymentElementsIterator<M> {
124
+ for (const ancestor of [...this.ancestors()].reverse()) {
125
+ yield* ancestor.siblings()
126
+ }
127
+ yield* this.siblings()
128
+ return
129
+ }
130
+
121
131
  public incoming(filter: IncomingFilter = 'all'): IteratorLike<DeploymentRelationModel<M>> {
122
132
  return this.$model.incoming(this, filter)
123
133
  }
@@ -153,6 +163,9 @@ export abstract class DeploymentElementModel<M extends AnyAux = AnyAux> {
153
163
  */
154
164
  public *views(): IteratorLike<LikeC4ViewModel<M, ComputedDeploymentView>> {
155
165
  for (const view of this.$model.views()) {
166
+ if (!view.isDeploymentView()) {
167
+ continue
168
+ }
156
169
  if (view.includesDeployment(this.id)) {
157
170
  yield view
158
171
  }
@@ -168,35 +181,46 @@ export abstract class DeploymentElementModel<M extends AnyAux = AnyAux> {
168
181
  return false
169
182
  }
170
183
 
171
- public abstract outgoingFromModel(): RelationshipsIterator<M>
172
- public abstract incomingFromModel(): RelationshipsIterator<M>
184
+ public abstract outgoingModelRelationships(): RelationshipsIterator<M>
185
+ public abstract incomingModelRelationships(): RelationshipsIterator<M>
173
186
 
174
187
  protected cachedOutgoing: RelationshipsAccum<M> | null = null
175
188
  protected cachedIncoming: RelationshipsAccum<M> | null = null
176
189
 
177
190
  public get allOutgoing(): RelationshipsAccum<M> {
178
- this.cachedOutgoing ??= new RelationshipsAccum(
179
- new Set(this.outgoingFromModel()),
180
- new Set(this.outgoing())
191
+ this.cachedOutgoing ??= RelationshipsAccum.from(
192
+ new Set(this.outgoingModelRelationships()),
193
+ new Set(this.outgoing()),
181
194
  )
182
195
  return this.cachedOutgoing
183
196
  }
184
197
 
185
198
  public get allIncoming(): RelationshipsAccum<M> {
186
- this.cachedIncoming ??= new RelationshipsAccum(
187
- new Set(this.incomingFromModel()),
188
- new Set(this.incoming())
199
+ this.cachedIncoming ??= RelationshipsAccum.from(
200
+ new Set(this.incomingModelRelationships()),
201
+ new Set(this.incoming()),
189
202
  )
190
203
  return this.cachedIncoming
191
204
  }
192
205
  }
193
206
 
194
- export class DeploymentNodeModel<M extends AnyAux = AnyAux> extends DeploymentElementModel<M> {
207
+ export class DeploymentNodeModel<M extends AnyAux = AnyAux> extends AbstractDeploymentElementModel<M> {
208
+ override id: M['DeploymentFqn']
209
+ override title: string
210
+ override hierarchyLevel: number
211
+
195
212
  constructor(
196
213
  public readonly $model: LikeC4DeploymentModel<M>,
197
- public readonly $node: DeploymentNode
214
+ public readonly $node: DeploymentNode,
198
215
  ) {
199
216
  super()
217
+ this.id = $node.id
218
+ this.title = $node.title
219
+ this.hierarchyLevel = hierarchyLevel($node.id)
220
+ }
221
+
222
+ get parent(): DeploymentNodeModel<M> | null {
223
+ return this.$model.parent(this)
200
224
  }
201
225
 
202
226
  get kind(): DeploymentNodeKind {
@@ -207,8 +231,8 @@ export class DeploymentNodeModel<M extends AnyAux = AnyAux> extends DeploymentEl
207
231
  return this.$model.children(this)
208
232
  }
209
233
 
210
- public descendants(): DeploymentElementsIterator<M> {
211
- return this.$model.descendants(this)
234
+ public descendants(sort: 'asc' | 'desc' = 'desc'): DeploymentElementsIterator<M> {
235
+ return this.$model.descendants(this, sort)
212
236
  }
213
237
 
214
238
  public override isDeploymentNode(): this is DeploymentNodeModel<M> {
@@ -219,7 +243,7 @@ export class DeploymentNodeModel<M extends AnyAux = AnyAux> extends DeploymentEl
219
243
  * Iterate over all instances nested in this deployment node.
220
244
  */
221
245
  public *instances(): DeployedInstancesIterator<M> {
222
- for (const nested of this.descendants()) {
246
+ for (const nested of this.descendants('desc')) {
223
247
  if (nested.isInstance()) {
224
248
  yield nested
225
249
  }
@@ -254,16 +278,16 @@ export class DeploymentNodeModel<M extends AnyAux = AnyAux> extends DeploymentEl
254
278
  }
255
279
  const {
256
280
  outgoing,
257
- incoming
281
+ incoming,
258
282
  } = (this._relationshipsFromInstances = {
259
283
  outgoing: new Set<RelationshipModel<M>>(),
260
- incoming: new Set<RelationshipModel<M>>()
284
+ incoming: new Set<RelationshipModel<M>>(),
261
285
  })
262
286
  for (const instance of this.instances()) {
263
- for (const r of instance.$element.outgoing()) {
287
+ for (const r of instance.element.outgoing()) {
264
288
  outgoing.add(r)
265
289
  }
266
- for (const r of instance.$element.incoming()) {
290
+ for (const r of instance.element.incoming()) {
267
291
  incoming.add(r)
268
292
  }
269
293
  }
@@ -273,98 +297,127 @@ export class DeploymentNodeModel<M extends AnyAux = AnyAux> extends DeploymentEl
273
297
  /**
274
298
  * We return only relationships that are not already present in nested instances
275
299
  */
276
- public override outgoingFromModel(): RelationshipsIterator<M> {
277
- const {
278
- outgoing,
279
- incoming
280
- } = this.relationshipsFromInstances()
281
- return difference(outgoing, incoming).values()
300
+ public override outgoingModelRelationships(): RelationshipsIterator<M> {
301
+ return this.relationshipsFromInstances().outgoing.values()
282
302
  }
283
303
 
284
304
  /**
285
305
  * We return only relationships that are not already present in nested instances
286
306
  */
287
- public override incomingFromModel(): RelationshipsIterator<M> {
307
+ public override incomingModelRelationships(): RelationshipsIterator<M> {
308
+ return this.relationshipsFromInstances().incoming.values()
309
+ }
310
+
311
+ /**
312
+ * Returns an iterator of relationships between nested instances
313
+ */
314
+ public internalModelRelationships(): ReadonlySet<RelationshipModel<M>> {
288
315
  const {
289
316
  outgoing,
290
- incoming
317
+ incoming,
291
318
  } = this.relationshipsFromInstances()
292
- return difference(incoming, outgoing).values()
319
+ return intersection(incoming, outgoing)
293
320
  }
294
321
  }
295
322
 
296
- export class DeployedInstanceModel<M extends AnyAux = AnyAux> extends DeploymentElementModel<M> {
323
+ export class DeployedInstanceModel<M extends AnyAux = AnyAux> extends AbstractDeploymentElementModel<M> {
324
+ override readonly id: M['DeploymentFqn']
325
+ override readonly title: string
326
+ override readonly hierarchyLevel: number
327
+
297
328
  constructor(
298
329
  public readonly $model: LikeC4DeploymentModel<M>,
299
330
  public readonly $instance: DeployedInstance,
300
- public readonly $element: ElementModel<M>
331
+ public readonly element: ElementModel<M>,
301
332
  ) {
302
333
  super()
303
- }
304
-
305
- override get parent(): DeploymentNodeModel<M> {
306
- return nonNullable(this.$model.parent(this), `Parent of ${this.id} not found`)
334
+ this.id = $instance.id
335
+ this.title = $instance.title ?? element.title
336
+ this.hierarchyLevel = hierarchyLevel($instance.id)
307
337
  }
308
338
 
309
339
  get $node(): DeployedInstance {
310
340
  return this.$instance
311
341
  }
312
342
 
313
- override get style(): SetRequired<PhysicalElementStyle, 'shape' | 'color'> {
314
- const { icon, style } = this.$element.$element
343
+ get parent(): DeploymentNodeModel<M> {
344
+ return nonNullable(this.$model.parent(this), `Parent of ${this.id} not found`)
345
+ }
346
+
347
+ override get style(): SetRequired<DeploymentElementStyle, 'shape' | 'color'> {
348
+ const { icon, style } = this.element.$element
315
349
  return {
316
- shape: this.$element.shape,
317
- color: this.$element.color,
350
+ shape: this.element.shape,
351
+ color: this.element.color,
318
352
  ...icon && { icon },
319
353
  ...style,
320
- ...this.$instance.style
354
+ ...this.$instance.style,
321
355
  }
322
356
  }
323
357
 
324
358
  override get shape(): C4ElementShape {
325
- return this.$instance.style?.shape ?? this.$element.shape
359
+ return this.$instance.style?.shape ?? this.element.shape
326
360
  }
327
361
 
328
362
  override get color(): ThemeColor {
329
- return this.$instance.style?.color as ThemeColor ?? this.$element.color
363
+ return this.$instance.style?.color as ThemeColor ?? this.element.color
330
364
  }
331
365
 
332
366
  override get tags(): ReadonlyArray<C4Tag> {
333
367
  return this.$instance.tags ?? []
334
368
  }
335
369
 
336
- override get title(): string {
337
- return this.$instance.title ?? this.$element.title
338
- }
339
-
340
370
  override get description(): string | null {
341
- return this.$instance.description ?? this.$element.description
371
+ return this.$instance.description ?? this.element.description
342
372
  }
343
373
 
344
374
  override get technology(): string | null {
345
- return this.$instance.technology ?? this.$element.technology
375
+ return this.$instance.technology ?? this.element.technology
346
376
  }
347
377
 
348
378
  override get links(): ReadonlyArray<Link> {
349
- return this.$instance.links ?? this.$element.links
379
+ return this.$instance.links ?? this.element.links
350
380
  }
351
381
 
352
382
  public override isInstance(): this is DeployedInstanceModel<M> {
353
383
  return true
354
384
  }
355
385
 
356
- public override outgoingFromModel(): RelationshipsIterator<M> {
357
- return this.$element.outgoing()
386
+ public override outgoingModelRelationships(): RelationshipsIterator<M> {
387
+ return this.element.outgoing()
358
388
  }
359
- public override incomingFromModel(): RelationshipsIterator<M> {
360
- return this.$element.incoming()
389
+ public override incomingModelRelationships(): RelationshipsIterator<M> {
390
+ return this.element.incoming()
391
+ }
392
+
393
+ /**
394
+ * Iterate over all views that include this instance.
395
+ * (Some views may include the parent deployment node instead of the instance.)
396
+ */
397
+ public override *views(): IteratorLike<LikeC4ViewModel<M, ComputedDeploymentView>> {
398
+ for (const view of this.$model.views()) {
399
+ if (!view.isDeploymentView()) {
400
+ continue
401
+ }
402
+ if (view.includesDeployment(this.id)) {
403
+ yield view
404
+ continue
405
+ }
406
+ // check if the view includes parent referecing this element
407
+ if (
408
+ view.includesDeployment(this.parent.id)
409
+ && this.parent.onlyOneInstance()
410
+ ) {
411
+ yield view
412
+ }
413
+ }
361
414
  }
362
415
  }
363
416
 
364
417
  export class NestedElementOfDeployedInstanceModel<M extends AnyAux> {
365
418
  constructor(
366
419
  public readonly instance: DeployedInstanceModel<M>,
367
- public readonly element: ElementModel<M>
420
+ public readonly element: ElementModel<M>,
368
421
  ) {
369
422
  }
370
423
 
@@ -372,13 +425,13 @@ export class NestedElementOfDeployedInstanceModel<M extends AnyAux> {
372
425
  return this.instance.id
373
426
  }
374
427
 
375
- get style(): SetRequired<PhysicalElementStyle, 'shape' | 'color'> {
428
+ get style(): SetRequired<DeploymentElementStyle, 'shape' | 'color'> {
376
429
  const { icon, style } = this.element.$element
377
430
  return {
378
431
  shape: this.element.shape,
379
432
  color: this.element.color,
380
433
  ...icon && { icon },
381
- ...style
434
+ ...style,
382
435
  }
383
436
  }
384
437
 
@@ -414,14 +467,14 @@ export type DeploymentRelationEndpoint<M extends AnyAux> =
414
467
  | DeploymentElementModel<M>
415
468
  | NestedElementOfDeployedInstanceModel<M>
416
469
 
417
- export class DeploymentRelationModel<M extends AnyAux> {
470
+ export class DeploymentRelationModel<M extends AnyAux = AnyAux> {
418
471
  public boundary: DeploymentNodeModel<M> | null
419
472
  public source: DeploymentRelationEndpoint<M>
420
473
  public target: DeploymentRelationEndpoint<M>
421
474
 
422
475
  constructor(
423
476
  public readonly $model: LikeC4DeploymentModel<M>,
424
- public readonly $relationship: DeploymentRelation
477
+ public readonly $relationship: DeploymentRelation,
425
478
  ) {
426
479
  this.source = $model.deploymentRef($relationship.source)
427
480
  this.target = $model.deploymentRef($relationship.target)
@@ -480,13 +533,25 @@ export class DeploymentRelationModel<M extends AnyAux> {
480
533
  }
481
534
 
482
535
  export class RelationshipsAccum<M extends AnyAux> {
536
+ static empty<M extends AnyAux>(): RelationshipsAccum<M> {
537
+ return new RelationshipsAccum()
538
+ }
539
+ static from<M extends AnyAux>(
540
+ model: Iterable<RelationshipModel<M>> | undefined,
541
+ deployment?: Iterable<DeploymentRelationModel<M>>,
542
+ ): RelationshipsAccum<M> {
543
+ return new RelationshipsAccum(
544
+ new Set(model),
545
+ new Set(deployment),
546
+ )
547
+ }
483
548
  /**
484
549
  * @param model relationships from logical model
485
550
  * @param deployment relationships from deployment model
486
551
  */
487
552
  constructor(
488
- public readonly model: ReadonlySet<RelationshipModel<M>>,
489
- public readonly deployment: ReadonlySet<DeploymentRelationModel<M>>
553
+ public readonly model: ReadonlySet<RelationshipModel<M>> = new Set(),
554
+ public readonly deployment: ReadonlySet<DeploymentRelationModel<M>> = new Set(),
490
555
  ) {
491
556
  }
492
557
 
@@ -506,9 +571,9 @@ export class RelationshipsAccum<M extends AnyAux> {
506
571
  * Returns new Accum containing all the elements which are both in this and otherAccum
507
572
  */
508
573
  public intersect(otherAccum: RelationshipsAccum<M>): RelationshipsAccum<M> {
509
- return new RelationshipsAccum(
574
+ return RelationshipsAccum.from(
510
575
  intersection(this.model, otherAccum.model),
511
- intersection(this.deployment, otherAccum.deployment)
576
+ intersection(this.deployment, otherAccum.deployment),
512
577
  )
513
578
  }
514
579
 
@@ -516,9 +581,9 @@ export class RelationshipsAccum<M extends AnyAux> {
516
581
  * Returns new Accum containing all the elements which are both in this and otherAccum
517
582
  */
518
583
  public difference(otherAccum: RelationshipsAccum<M>): RelationshipsAccum<M> {
519
- return new RelationshipsAccum(
584
+ return RelationshipsAccum.from(
520
585
  difference(this.model, otherAccum.model),
521
- difference(this.deployment, otherAccum.deployment)
586
+ difference(this.deployment, otherAccum.deployment),
522
587
  )
523
588
  }
524
589
 
@@ -526,9 +591,9 @@ export class RelationshipsAccum<M extends AnyAux> {
526
591
  * Returns new Accum containing all the elements from both
527
592
  */
528
593
  public union(otherAccum: RelationshipsAccum<M>): RelationshipsAccum<M> {
529
- return new RelationshipsAccum(
594
+ return RelationshipsAccum.from(
530
595
  union(this.model, otherAccum.model),
531
- union(this.deployment, otherAccum.deployment)
596
+ union(this.deployment, otherAccum.deployment),
532
597
  )
533
598
  }
534
599
  }
@@ -3,28 +3,29 @@ import { values } from 'remeda'
3
3
  import type { LiteralUnion } from 'type-fest'
4
4
  import { invariant, nonNullable } from '../errors'
5
5
  import {
6
- type AnyLikeC4Model,
7
6
  type ComputedDeploymentView,
8
- DeploymentElement,
9
7
  type DeploymentRef,
10
8
  type DeploymentRelation,
11
- type Tag as C4Tag
9
+ type GenericLikeC4Model,
10
+ type IteratorLike,
11
+ type Tag as C4Tag,
12
+ DeploymentElement,
12
13
  } from '../types'
13
- import { ancestorsFqn, parentFqn } from '../utils/fqn'
14
+ import { ancestorsFqn, parentFqn, sortParentsFirst } from '../utils/fqn'
14
15
  import { getOrCreate } from '../utils/getOrCreate'
15
16
  import { isString } from '../utils/guards'
16
17
  import {
17
- DeployedInstanceModel,
18
18
  type DeployedInstancesIterator,
19
- DeploymentElementModel,
19
+ type DeploymentElementModel,
20
20
  type DeploymentElementsIterator,
21
- DeploymentNodeModel,
22
21
  type DeploymentNodesIterator,
22
+ DeployedInstanceModel,
23
+ DeploymentNodeModel,
23
24
  DeploymentRelationModel,
24
- NestedElementOfDeployedInstanceModel
25
+ NestedElementOfDeployedInstanceModel,
25
26
  } from './DeploymentElementModel'
26
27
  import type { LikeC4Model } from './LikeC4Model'
27
- import { type AnyAux, getId, type IncomingFilter, type IteratorLike, type OutgoingFilter } from './types'
28
+ import { type AnyAux, type IncomingFilter, type OutgoingFilter, getId } from './types'
28
29
  import type { LikeC4ViewModel } from './view/LikeC4ViewModel'
29
30
 
30
31
  export class LikeC4DeploymentModel<M extends AnyAux = AnyAux> {
@@ -63,15 +64,15 @@ export class LikeC4DeploymentModel<M extends AnyAux = AnyAux> {
63
64
 
64
65
  constructor(
65
66
  public readonly $model: LikeC4Model<M>,
66
- public readonly $deployments: AnyLikeC4Model['deployments']
67
+ public readonly $deployments: GenericLikeC4Model['deployments'],
67
68
  ) {
68
- for (const element of values($deployments.elements)) {
69
+ for (const element of sortParentsFirst(values($deployments.elements))) {
69
70
  const el = this.addElement(element)
70
71
  for (const tag of el.tags) {
71
72
  this.#allTags.get(tag).add(el)
72
73
  }
73
74
  if (el.isInstance()) {
74
- this.#instancesOf.get(el.$element.id).add(el)
75
+ this.#instancesOf.get(el.element.id).add(el)
75
76
  }
76
77
  }
77
78
  for (const relation of values($deployments.relations)) {
@@ -83,7 +84,7 @@ export class LikeC4DeploymentModel<M extends AnyAux = AnyAux> {
83
84
  }
84
85
 
85
86
  public element(el: M['DeploymentOrFqn']): DeploymentElementModel<M> {
86
- if (el instanceof DeploymentElementModel) {
87
+ if (el instanceof DeploymentNodeModel || el instanceof DeployedInstanceModel) {
87
88
  return el
88
89
  }
89
90
  const id = getId(el)
@@ -256,10 +257,15 @@ export class LikeC4DeploymentModel<M extends AnyAux = AnyAux> {
256
257
  /**
257
258
  * Get all descendant elements (i.e. children, children’s children, etc.)
258
259
  */
259
- public *descendants(element: M['DeploymentOrFqn']): DeploymentElementsIterator<M> {
260
+ public *descendants(element: M['DeploymentOrFqn'], sort: 'asc' | 'desc' = 'desc'): DeploymentElementsIterator<M> {
260
261
  for (const child of this.children(element)) {
261
- yield child
262
- yield* this.descendants(child.id)
262
+ if (sort === 'asc') {
263
+ yield child
264
+ yield* this.descendants(child.id)
265
+ } else {
266
+ yield* this.descendants(child.id)
267
+ yield child
268
+ }
263
269
  }
264
270
  return
265
271
  }
@@ -270,7 +276,7 @@ export class LikeC4DeploymentModel<M extends AnyAux = AnyAux> {
270
276
  */
271
277
  public *incoming(
272
278
  element: M['DeploymentOrFqn'],
273
- filter: IncomingFilter = 'all'
279
+ filter: IncomingFilter = 'all',
274
280
  ): IteratorLike<DeploymentRelationModel<M>> {
275
281
  const id = getId(element)
276
282
  for (const rel of this.#incoming.get(id)) {
@@ -291,7 +297,7 @@ export class LikeC4DeploymentModel<M extends AnyAux = AnyAux> {
291
297
  */
292
298
  public *outgoing(
293
299
  element: M['DeploymentOrFqn'],
294
- filter: OutgoingFilter = 'all'
300
+ filter: OutgoingFilter = 'all',
295
301
  ): IteratorLike<DeploymentRelationModel<M>> {
296
302
  const id = getId(element)
297
303
  for (const rel of this.#outgoing.get(id)) {
@@ -311,8 +317,8 @@ export class LikeC4DeploymentModel<M extends AnyAux = AnyAux> {
311
317
  throw new Error(`Element ${element.id} already exists`)
312
318
  }
313
319
  const el = DeploymentElement.isDeploymentNode(element)
314
- ? new DeploymentNodeModel(this, element)
315
- : new DeployedInstanceModel(this, element, this.$model.element(element.element))
320
+ ? new DeploymentNodeModel(this, Object.freeze(element))
321
+ : new DeployedInstanceModel(this, Object.freeze(element), this.$model.element(element.element))
316
322
  this.#elements.set(el.id, el)
317
323
  const parentId = parentFqn(el.id)
318
324
  if (parentId) {
@@ -332,7 +338,7 @@ export class LikeC4DeploymentModel<M extends AnyAux = AnyAux> {
332
338
  }
333
339
  const rel = new DeploymentRelationModel(
334
340
  this,
335
- relation
341
+ Object.freeze(relation),
336
342
  )
337
343
  this.#relations.set(rel.id, rel)
338
344
  this.#incoming.get(rel.target.id).add(rel)