@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,36 @@
1
+ import { map, mapToObj, mapValues, omit } from 'remeda'
2
+ import type { ComputedLikeC4Model } from '../../types'
3
+ import type { ComputedView, EdgeId } from '../../types/view'
4
+
5
+ /**
6
+ * Convert hashed edge ids to human-readable
7
+ * Mostly for testing purposes
8
+ */
9
+ export function withReadableEdges<T extends ComputedView>({ edges, nodes, ...view }: T, separator = ':'): T & {
10
+ nodeIds: string[]
11
+ edgeIds: string[]
12
+ } {
13
+ const edgeids = mapToObj(edges, e => [e.id, `${e.source}${separator}${e.target}` as EdgeId])
14
+
15
+ return {
16
+ ...view,
17
+ edges: edges.map(e => ({
18
+ ...e,
19
+ id: edgeids[e.id]!,
20
+ })),
21
+ nodes: nodes.map(n => ({
22
+ ...n,
23
+ inEdges: map(n.inEdges, e => edgeids[e]!),
24
+ outEdges: map(n.outEdges, e => edgeids[e]!),
25
+ })),
26
+ nodeIds: nodes.map(n => n.id),
27
+ edgeIds: edges.map(e => edgeids[e.id]!),
28
+ } as any
29
+ }
30
+
31
+ export function viewsWithReadableEdges<M extends ComputedLikeC4Model>({ views, ...model }: M): M {
32
+ return {
33
+ ...model,
34
+ views: mapValues(views, v => omit(withReadableEdges(v), ['nodeIds', 'edgeIds'])),
35
+ } as any
36
+ }
package/src/index.ts CHANGED
@@ -1,16 +1,26 @@
1
- export * from './builder'
2
- export { Builder } from './builder'
3
- export type * from './builder'
4
- export * from './colors'
5
- export * from './errors'
6
- export * from './model'
1
+ export {
2
+ computeColorValues,
3
+ defaultTheme,
4
+ ElementColors,
5
+ RelationshipColors,
6
+ } from './colors'
7
+
8
+ export {
9
+ invariant,
10
+ nonexhaustive,
11
+ nonNullable,
12
+ } from './errors'
13
+
14
+ export {
15
+ type AnyAux,
16
+ type Aux,
17
+ LikeC4DeploymentModel,
18
+ LikeC4Model,
19
+ LikeC4ViewModel,
20
+ } from './model'
7
21
 
8
22
  export * from './types'
9
- export * from './utils/commonHead'
10
- export * from './utils/compare-natural'
11
- export * from './utils/fqn'
12
- export * from './utils/guards'
13
- export * from './utils/promises'
14
- export { compareRelations } from './utils/relations'
23
+
24
+ export * from './utils'
15
25
 
16
26
  export * as Expr from './types/expression'
@@ -0,0 +1,599 @@
1
+ import { isEmpty, only } from 'remeda'
2
+ import type { SetRequired } from 'type-fest'
3
+ import { nonNullable } from '../errors'
4
+ import {
5
+ type ComputedDeploymentView,
6
+ type DeployedInstance,
7
+ type DeploymentElementStyle,
8
+ type DeploymentNode,
9
+ type DeploymentNodeKind,
10
+ type DeploymentRelation,
11
+ type ElementShape as C4ElementShape,
12
+ type IteratorLike,
13
+ type Link,
14
+ type Tag,
15
+ type Tag as C4Tag,
16
+ type ThemeColor,
17
+ DefaultElementShape,
18
+ DefaultThemeColor,
19
+ } from '../types'
20
+ import { commonAncestor, hierarchyLevel } from '../utils'
21
+ import { difference, intersection, union } from '../utils/set'
22
+ import type { LikeC4DeploymentModel } from './DeploymentModel'
23
+ import type { ElementModel } from './ElementModel'
24
+ import type { RelationshipModel, RelationshipsIterator } from './RelationModel'
25
+ import type { AnyAux, IncomingFilter, OutgoingFilter } from './types'
26
+ import type { LikeC4ViewModel } from './view/LikeC4ViewModel'
27
+
28
+ export type DeploymentElementsIterator<M extends AnyAux> = IteratorLike<
29
+ DeploymentNodeModel<M> | DeployedInstanceModel<M>
30
+ >
31
+ export type DeployedInstancesIterator<M extends AnyAux> = IteratorLike<DeployedInstanceModel<M>>
32
+ export type DeploymentNodesIterator<M extends AnyAux> = IteratorLike<DeploymentNodeModel<M>>
33
+
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
+
42
+ abstract readonly $model: LikeC4DeploymentModel<M>
43
+ abstract readonly $node: DeploymentNode | DeployedInstance
44
+
45
+ get style(): SetRequired<DeploymentElementStyle, 'shape' | 'color'> {
46
+ return {
47
+ shape: DefaultElementShape,
48
+ color: DefaultThemeColor,
49
+ ...this.$node.style,
50
+ }
51
+ }
52
+
53
+ get shape(): C4ElementShape {
54
+ return this.$node.style?.shape ?? DefaultElementShape
55
+ }
56
+
57
+ get color(): ThemeColor {
58
+ return this.$node.style?.color as ThemeColor ?? DefaultThemeColor
59
+ }
60
+
61
+ get tags(): ReadonlyArray<C4Tag> {
62
+ return this.$node.tags ?? []
63
+ }
64
+
65
+ get description(): string | null {
66
+ return this.$node.description ?? null
67
+ }
68
+
69
+ get technology(): string | null {
70
+ return this.$node.technology ?? null
71
+ }
72
+
73
+ get links(): ReadonlyArray<Link> {
74
+ return this.$node.links ?? []
75
+ }
76
+
77
+ /**
78
+ * Get all ancestor elements (i.e. parent, parent’s parent, etc.)
79
+ * (from closest to root)
80
+ */
81
+ public ancestors(): DeploymentNodesIterator<M> {
82
+ return this.$model.ancestors(this)
83
+ }
84
+
85
+ /**
86
+ * Returns the common ancestor of this element and another element.
87
+ */
88
+ public commonAncestor(another: DeploymentElementModel<M>): DeploymentNodeModel<M> | null {
89
+ const common = commonAncestor(this.id, another.id)
90
+ return common ? this.$model.node(common) : null
91
+ }
92
+
93
+ /**
94
+ * Get all sibling (i.e. same parent)
95
+ */
96
+ public siblings(): DeploymentElementsIterator<M> {
97
+ return this.$model.siblings(this)
98
+ }
99
+
100
+ /**
101
+ * Check if the element is a sibling of another element
102
+ */
103
+ public isSibling(other: DeploymentElementModel<M>): boolean {
104
+ return this.parent === other.parent
105
+ }
106
+
107
+ /**
108
+ * Resolve siblings of the element and its ancestors
109
+ * (from closest to root)
110
+ */
111
+ public *ascendingSiblings(): DeploymentElementsIterator<M> {
112
+ yield* this.siblings()
113
+ for (const ancestor of this.ancestors()) {
114
+ yield* ancestor.siblings()
115
+ }
116
+ return
117
+ }
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
+
131
+ public incoming(filter: IncomingFilter = 'all'): IteratorLike<DeploymentRelationModel<M>> {
132
+ return this.$model.incoming(this, filter)
133
+ }
134
+ public outgoing(filter: OutgoingFilter = 'all'): IteratorLike<DeploymentRelationModel<M>> {
135
+ return this.$model.outgoing(this, filter)
136
+ }
137
+
138
+ public *incomers(filter: IncomingFilter = 'all'): IteratorLike<DeploymentRelationEndpoint<M>> {
139
+ const unique = new Set<M['Deployment']>()
140
+ for (const r of this.incoming(filter)) {
141
+ if (unique.has(r.source.id)) {
142
+ continue
143
+ }
144
+ unique.add(r.source.id)
145
+ yield r.source
146
+ }
147
+ return
148
+ }
149
+ public *outgoers(filter: OutgoingFilter = 'all'): IteratorLike<DeploymentRelationEndpoint<M>> {
150
+ const unique = new Set<M['Deployment']>()
151
+ for (const r of this.outgoing(filter)) {
152
+ if (unique.has(r.target.id)) {
153
+ continue
154
+ }
155
+ unique.add(r.target.id)
156
+ yield r.target
157
+ }
158
+ return
159
+ }
160
+
161
+ /**
162
+ * Iterate over all views that include this deployment element.
163
+ */
164
+ public *views(): IteratorLike<LikeC4ViewModel<M, ComputedDeploymentView>> {
165
+ for (const view of this.$model.views()) {
166
+ if (!view.isDeploymentView()) {
167
+ continue
168
+ }
169
+ if (view.includesDeployment(this.id)) {
170
+ yield view
171
+ }
172
+ }
173
+ }
174
+
175
+ // type guard
176
+ public isDeploymentNode(): this is DeploymentNodeModel<M> {
177
+ return false
178
+ }
179
+ // type guard
180
+ public isInstance(): this is DeployedInstanceModel<M> {
181
+ return false
182
+ }
183
+
184
+ public abstract outgoingModelRelationships(): RelationshipsIterator<M>
185
+ public abstract incomingModelRelationships(): RelationshipsIterator<M>
186
+
187
+ protected cachedOutgoing: RelationshipsAccum<M> | null = null
188
+ protected cachedIncoming: RelationshipsAccum<M> | null = null
189
+
190
+ public get allOutgoing(): RelationshipsAccum<M> {
191
+ this.cachedOutgoing ??= RelationshipsAccum.from(
192
+ new Set(this.outgoingModelRelationships()),
193
+ new Set(this.outgoing()),
194
+ )
195
+ return this.cachedOutgoing
196
+ }
197
+
198
+ public get allIncoming(): RelationshipsAccum<M> {
199
+ this.cachedIncoming ??= RelationshipsAccum.from(
200
+ new Set(this.incomingModelRelationships()),
201
+ new Set(this.incoming()),
202
+ )
203
+ return this.cachedIncoming
204
+ }
205
+ }
206
+
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
+
212
+ constructor(
213
+ public readonly $model: LikeC4DeploymentModel<M>,
214
+ public readonly $node: DeploymentNode,
215
+ ) {
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)
224
+ }
225
+
226
+ get kind(): DeploymentNodeKind {
227
+ return this.$node.kind
228
+ }
229
+
230
+ public children(): ReadonlySet<DeploymentElementModel<M>> {
231
+ return this.$model.children(this)
232
+ }
233
+
234
+ public descendants(sort: 'asc' | 'desc' = 'desc'): DeploymentElementsIterator<M> {
235
+ return this.$model.descendants(this, sort)
236
+ }
237
+
238
+ public override isDeploymentNode(): this is DeploymentNodeModel<M> {
239
+ return true
240
+ }
241
+
242
+ /**
243
+ * Iterate over all instances nested in this deployment node.
244
+ */
245
+ public *instances(): DeployedInstancesIterator<M> {
246
+ for (const nested of this.descendants('desc')) {
247
+ if (nested.isInstance()) {
248
+ yield nested
249
+ }
250
+ }
251
+ return
252
+ }
253
+
254
+ /**
255
+ * Returns deployed instance inside this deployment node
256
+ * if only there are no more instances
257
+ */
258
+ public onlyOneInstance(): DeployedInstanceModel<M> | null {
259
+ const children = this.children()
260
+ if (children.size !== 1) {
261
+ return null
262
+ }
263
+ const child = only([...children])
264
+ return child?.isInstance() ? child : null
265
+ }
266
+
267
+ /**
268
+ * Cached result of relationships from instances
269
+ */
270
+ private _relationshipsFromInstances: {
271
+ outgoing: ReadonlySet<RelationshipModel<M>>
272
+ incoming: ReadonlySet<RelationshipModel<M>>
273
+ } | null = null
274
+
275
+ private relationshipsFromInstances() {
276
+ if (this._relationshipsFromInstances) {
277
+ return this._relationshipsFromInstances
278
+ }
279
+ const {
280
+ outgoing,
281
+ incoming,
282
+ } = (this._relationshipsFromInstances = {
283
+ outgoing: new Set<RelationshipModel<M>>(),
284
+ incoming: new Set<RelationshipModel<M>>(),
285
+ })
286
+ for (const instance of this.instances()) {
287
+ for (const r of instance.element.outgoing()) {
288
+ outgoing.add(r)
289
+ }
290
+ for (const r of instance.element.incoming()) {
291
+ incoming.add(r)
292
+ }
293
+ }
294
+ return this._relationshipsFromInstances
295
+ }
296
+
297
+ /**
298
+ * We return only relationships that are not already present in nested instances
299
+ */
300
+ public override outgoingModelRelationships(): RelationshipsIterator<M> {
301
+ return this.relationshipsFromInstances().outgoing.values()
302
+ }
303
+
304
+ /**
305
+ * We return only relationships that are not already present in nested instances
306
+ */
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>> {
315
+ const {
316
+ outgoing,
317
+ incoming,
318
+ } = this.relationshipsFromInstances()
319
+ return intersection(incoming, outgoing)
320
+ }
321
+ }
322
+
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
+
328
+ constructor(
329
+ public readonly $model: LikeC4DeploymentModel<M>,
330
+ public readonly $instance: DeployedInstance,
331
+ public readonly element: ElementModel<M>,
332
+ ) {
333
+ super()
334
+ this.id = $instance.id
335
+ this.title = $instance.title ?? element.title
336
+ this.hierarchyLevel = hierarchyLevel($instance.id)
337
+ }
338
+
339
+ get $node(): DeployedInstance {
340
+ return this.$instance
341
+ }
342
+
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
349
+ return {
350
+ shape: this.element.shape,
351
+ color: this.element.color,
352
+ ...icon && { icon },
353
+ ...style,
354
+ ...this.$instance.style,
355
+ }
356
+ }
357
+
358
+ override get shape(): C4ElementShape {
359
+ return this.$instance.style?.shape ?? this.element.shape
360
+ }
361
+
362
+ override get color(): ThemeColor {
363
+ return this.$instance.style?.color as ThemeColor ?? this.element.color
364
+ }
365
+
366
+ override get tags(): ReadonlyArray<C4Tag> {
367
+ return this.$instance.tags ?? []
368
+ }
369
+
370
+ override get description(): string | null {
371
+ return this.$instance.description ?? this.element.description
372
+ }
373
+
374
+ override get technology(): string | null {
375
+ return this.$instance.technology ?? this.element.technology
376
+ }
377
+
378
+ override get links(): ReadonlyArray<Link> {
379
+ return this.$instance.links ?? this.element.links
380
+ }
381
+
382
+ public override isInstance(): this is DeployedInstanceModel<M> {
383
+ return true
384
+ }
385
+
386
+ public override outgoingModelRelationships(): RelationshipsIterator<M> {
387
+ return this.element.outgoing()
388
+ }
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
+ }
414
+ }
415
+ }
416
+
417
+ export class NestedElementOfDeployedInstanceModel<M extends AnyAux> {
418
+ constructor(
419
+ public readonly instance: DeployedInstanceModel<M>,
420
+ public readonly element: ElementModel<M>,
421
+ ) {
422
+ }
423
+
424
+ get id(): M['DeploymentFqn'] {
425
+ return this.instance.id
426
+ }
427
+
428
+ get style(): SetRequired<DeploymentElementStyle, 'shape' | 'color'> {
429
+ const { icon, style } = this.element.$element
430
+ return {
431
+ shape: this.element.shape,
432
+ color: this.element.color,
433
+ ...icon && { icon },
434
+ ...style,
435
+ }
436
+ }
437
+
438
+ get shape(): C4ElementShape {
439
+ return this.element.shape
440
+ }
441
+
442
+ get color(): ThemeColor {
443
+ return this.element.color
444
+ }
445
+
446
+ get title(): string {
447
+ return this.element.title
448
+ }
449
+
450
+ get description(): string | null {
451
+ return this.element.description
452
+ }
453
+
454
+ get technology(): string | null {
455
+ return this.element.technology
456
+ }
457
+
458
+ public isDeploymentNode(): this is DeploymentNodeModel<M> {
459
+ return false
460
+ }
461
+ public isInstance(): this is DeployedInstanceModel<M> {
462
+ return false
463
+ }
464
+ }
465
+
466
+ export type DeploymentRelationEndpoint<M extends AnyAux> =
467
+ | DeploymentElementModel<M>
468
+ | NestedElementOfDeployedInstanceModel<M>
469
+
470
+ export class DeploymentRelationModel<M extends AnyAux = AnyAux> {
471
+ public boundary: DeploymentNodeModel<M> | null
472
+ public source: DeploymentRelationEndpoint<M>
473
+ public target: DeploymentRelationEndpoint<M>
474
+
475
+ constructor(
476
+ public readonly $model: LikeC4DeploymentModel<M>,
477
+ public readonly $relationship: DeploymentRelation,
478
+ ) {
479
+ this.source = $model.deploymentRef($relationship.source)
480
+ this.target = $model.deploymentRef($relationship.target)
481
+ const parent = commonAncestor(this.source.id, this.target.id)
482
+ this.boundary = parent ? this.$model.node(parent) : null
483
+ }
484
+ get id(): M['RelationId'] {
485
+ return this.$relationship.id
486
+ }
487
+
488
+ get expression(): string {
489
+ return `${this.source.id} -> ${this.target.id}`
490
+ }
491
+
492
+ get title(): string | null {
493
+ if (isEmpty(this.$relationship.title)) {
494
+ return null
495
+ }
496
+ return this.$relationship.title
497
+ }
498
+
499
+ get technology(): string | null {
500
+ if (isEmpty(this.$relationship.technology)) {
501
+ return null
502
+ }
503
+ return this.$relationship.technology
504
+ }
505
+
506
+ get description(): string | null {
507
+ if (isEmpty(this.$relationship.description)) {
508
+ return null
509
+ }
510
+ return this.$relationship.description
511
+ }
512
+
513
+ get tags(): ReadonlyArray<Tag> {
514
+ return this.$relationship.tags ?? []
515
+ }
516
+
517
+ get navigateTo(): LikeC4ViewModel<M> | null {
518
+ return this.$relationship.navigateTo ? this.$model.$model.view(this.$relationship.navigateTo) : null
519
+ }
520
+
521
+ get links(): ReadonlyArray<Link> {
522
+ return this.$relationship.links ?? []
523
+ }
524
+
525
+ public *views(): IteratorLike<LikeC4ViewModel<M, ComputedDeploymentView>> {
526
+ for (const view of this.$model.views()) {
527
+ if (view.includesRelation(this.id)) {
528
+ yield view
529
+ }
530
+ }
531
+ return
532
+ }
533
+ }
534
+
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
+ }
548
+ /**
549
+ * @param model relationships from logical model
550
+ * @param deployment relationships from deployment model
551
+ */
552
+ constructor(
553
+ public readonly model: ReadonlySet<RelationshipModel<M>> = new Set(),
554
+ public readonly deployment: ReadonlySet<DeploymentRelationModel<M>> = new Set(),
555
+ ) {
556
+ }
557
+
558
+ get isEmpty(): boolean {
559
+ return this.model.size === 0 && this.deployment.size === 0
560
+ }
561
+
562
+ get nonEmpty(): boolean {
563
+ return this.model.size > 0 || this.deployment.size > 0
564
+ }
565
+
566
+ get size(): number {
567
+ return this.model.size + this.deployment.size
568
+ }
569
+
570
+ /**
571
+ * Returns new Accum containing all the elements which are both in this and otherAccum
572
+ */
573
+ public intersect(otherAccum: RelationshipsAccum<M>): RelationshipsAccum<M> {
574
+ return RelationshipsAccum.from(
575
+ intersection(this.model, otherAccum.model),
576
+ intersection(this.deployment, otherAccum.deployment),
577
+ )
578
+ }
579
+
580
+ /**
581
+ * Returns new Accum containing all the elements which are both in this and otherAccum
582
+ */
583
+ public difference(otherAccum: RelationshipsAccum<M>): RelationshipsAccum<M> {
584
+ return RelationshipsAccum.from(
585
+ difference(this.model, otherAccum.model),
586
+ difference(this.deployment, otherAccum.deployment),
587
+ )
588
+ }
589
+
590
+ /**
591
+ * Returns new Accum containing all the elements from both
592
+ */
593
+ public union(otherAccum: RelationshipsAccum<M>): RelationshipsAccum<M> {
594
+ return RelationshipsAccum.from(
595
+ union(this.model, otherAccum.model),
596
+ union(this.deployment, otherAccum.deployment),
597
+ )
598
+ }
599
+ }