@likec4/core 1.17.1 → 1.18.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 (112) hide show
  1. package/dist/compute-view/index.d.mts +91 -0
  2. package/dist/compute-view/index.d.ts +91 -0
  3. package/dist/compute-view/index.mjs +79 -0
  4. package/dist/index.d.mts +246 -434
  5. package/dist/index.d.ts +246 -434
  6. package/dist/index.mjs +418 -1322
  7. package/dist/model/index.d.mts +642 -0
  8. package/dist/model/index.d.ts +642 -0
  9. package/dist/model/index.mjs +2 -0
  10. package/dist/shared/core.BBgiAAv1.mjs +144 -0
  11. package/dist/shared/core.BPyJvuMU.mjs +2420 -0
  12. package/dist/shared/core.BQ4AbfvB.mjs +9228 -0
  13. package/dist/shared/{core.CoLQkcYL.d.mts → core.DMYY7C8s.d.mts} +398 -249
  14. package/dist/shared/{core.CoLQkcYL.d.ts → core.DMYY7C8s.d.ts} +398 -249
  15. package/dist/shared/core.k12s_keD.mjs +203 -0
  16. package/dist/types/index.d.mts +69 -1
  17. package/dist/types/index.d.ts +69 -1
  18. package/dist/types/index.mjs +21 -1
  19. package/package.json +38 -40
  20. package/src/builder/Builder.deployment.ts +219 -0
  21. package/src/builder/Builder.deploymentModel.ts +360 -0
  22. package/src/builder/Builder.element.ts +17 -19
  23. package/src/builder/Builder.model.ts +13 -2
  24. package/src/builder/Builder.test-d.ts +59 -4
  25. package/src/builder/Builder.ts +238 -53
  26. package/src/builder/Builder.view.ts +238 -53
  27. package/src/builder/_types.ts +87 -15
  28. package/src/compute-view/LikeC4ModelGraph.ts +330 -0
  29. package/src/compute-view/compute-view.ts +92 -0
  30. package/src/compute-view/deployment-view/Memory.ts +108 -0
  31. package/src/compute-view/deployment-view/Stage.ts +145 -0
  32. package/src/compute-view/deployment-view/__test__/fixture.ts +210 -0
  33. package/src/compute-view/deployment-view/_types.ts +29 -0
  34. package/src/compute-view/deployment-view/clean-connections.ts +229 -0
  35. package/src/compute-view/deployment-view/compute.ts +111 -0
  36. package/src/compute-view/deployment-view/predicates/deployment-ref.ts +146 -0
  37. package/src/compute-view/deployment-view/predicates/relation-direct.ts +130 -0
  38. package/src/compute-view/deployment-view/predicates/relation-in-out.ts +43 -0
  39. package/src/compute-view/deployment-view/predicates/relation-incoming.ts +46 -0
  40. package/src/compute-view/deployment-view/predicates/relation-outgoing.ts +49 -0
  41. package/src/compute-view/deployment-view/predicates/wildcard.ts +29 -0
  42. package/src/compute-view/deployment-view/utils.ts +212 -0
  43. package/src/compute-view/dynamic-view/__test__/fixture.ts +58 -0
  44. package/src/compute-view/dynamic-view/compute.ts +310 -0
  45. package/src/compute-view/element-view/__test__/__snapshots__/legacy.spec.ts.snap +456 -0
  46. package/src/compute-view/element-view/__test__/fixture.ts +645 -0
  47. package/src/compute-view/element-view/compute.ts +734 -0
  48. package/src/compute-view/element-view/predicates.ts +517 -0
  49. package/src/compute-view/index.ts +5 -0
  50. package/src/compute-view/utils/__snapshots__/sortNodes.spec.ts.snap +113 -0
  51. package/src/compute-view/utils/ancestorsOfNode.ts +19 -0
  52. package/src/compute-view/utils/applyCustomElementProperties.ts +66 -0
  53. package/src/compute-view/utils/applyCustomRelationProperties.ts +41 -0
  54. package/src/compute-view/utils/applyViewRuleStyles.ts +53 -0
  55. package/src/compute-view/utils/buildComputedNodes.ts +148 -0
  56. package/src/compute-view/utils/buildElementNotations.ts +63 -0
  57. package/src/compute-view/utils/derive-edge-props-from-relationships.ts +97 -0
  58. package/src/compute-view/utils/elementExpressionToPredicate.ts +52 -0
  59. package/src/compute-view/utils/link-nodes-with-edges.ts +58 -0
  60. package/src/compute-view/utils/relationExpressionToPredicates.ts +55 -0
  61. package/src/compute-view/utils/resolve-extended-views.ts +65 -0
  62. package/src/compute-view/utils/resolve-global-rules.ts +83 -0
  63. package/src/compute-view/utils/sortNodes.ts +101 -0
  64. package/src/compute-view/utils/topological-sort.ts +160 -0
  65. package/src/compute-view/utils/uniqueTags.test.ts +42 -0
  66. package/src/compute-view/utils/uniqueTags.ts +21 -0
  67. package/src/compute-view/utils/view-hash.ts +27 -0
  68. package/src/compute-view/utils/with-readable-edges.ts +31 -0
  69. package/src/index.ts +5 -1
  70. package/src/model/DeploymentElementModel.ts +534 -0
  71. package/src/model/DeploymentModel.ts +364 -0
  72. package/src/model/ElementModel.ts +195 -0
  73. package/src/model/LikeC4Model.test-d.ts +151 -0
  74. package/src/model/LikeC4Model.ts +280 -457
  75. package/src/model/RelationModel.ts +84 -0
  76. package/src/model/__test__/fixture.ts +153 -338
  77. package/src/model/connection/ConnectionModel.ts +94 -0
  78. package/src/model/connection/DeploymentConnectionModel.ts +99 -0
  79. package/src/model/connection/deployment.ts +124 -0
  80. package/src/model/connection/model.ts +123 -0
  81. package/src/model/index.ts +4 -2
  82. package/src/model/types.ts +95 -15
  83. package/src/model/view/EdgeModel.ts +103 -0
  84. package/src/model/view/LikeC4ViewModel.ts +220 -0
  85. package/src/model/view/NodeModel.ts +228 -0
  86. package/src/types/_common.ts +2 -0
  87. package/src/types/deployments.ts +237 -0
  88. package/src/types/element.ts +7 -3
  89. package/src/types/expression.ts +8 -6
  90. package/src/types/index.ts +1 -0
  91. package/src/types/model.ts +68 -9
  92. package/src/types/overview-graph.ts +2 -2
  93. package/src/types/relation.ts +4 -4
  94. package/src/types/view-notation.ts +2 -2
  95. package/src/types/view.ts +104 -30
  96. package/src/utils/{commonHead.ts → common-head.ts} +2 -0
  97. package/src/utils/fqn.ts +83 -27
  98. package/src/utils/getOrCreate.ts +42 -0
  99. package/src/utils/graphlib.ts +10 -0
  100. package/src/utils/index.ts +4 -1
  101. package/src/utils/object-hash.ts +3 -0
  102. package/src/utils/set.ts +76 -0
  103. package/src/utils/string-hash.ts +16 -0
  104. package/dist/index.cjs +0 -5452
  105. package/dist/index.d.cts +0 -1053
  106. package/dist/shared/core.BXTs-8n7.mjs +0 -293
  107. package/dist/shared/core.CoLQkcYL.d.cts +0 -734
  108. package/dist/shared/core.DIYZvpVg.cjs +0 -353
  109. package/dist/types/index.cjs +0 -70
  110. package/dist/types/index.d.cts +0 -2
  111. package/src/model/LikeC4DiagramModel.ts +0 -338
  112. package/src/model/LikeC4ViewModel.ts +0 -320
@@ -1,338 +0,0 @@
1
- import { isNullish } from 'remeda'
2
- import { nonNullable } from '../errors'
3
- import {
4
- type ElementKind as C4ElementKind,
5
- type ElementShape as C4ElementShape,
6
- type Tag as C4Tag
7
- } from '../types/element'
8
- import { DefaultRelationshipColor } from '../types/relation'
9
- import type { Color as C4Color } from '../types/theme'
10
- import type { ComputedElementView, DiagramEdge, DiagramNode, DiagramView } from '../types/view'
11
- import type { LikeC4Model } from './LikeC4Model'
12
- import { type EdgeId, type ElementOrFqn, type Fqn, getId, type IncomingFilter, type OutgoingFilter } from './types'
13
-
14
- /**
15
- * All methods are view-scoped, i.e. only return elements and connections in the view.
16
- */
17
- export class LikeC4DiagramModel {
18
- private readonly _rootElements = new Set<LikeC4DiagramModel.Element>()
19
-
20
- private readonly _elements = new Map<Fqn, LikeC4DiagramModel.Element>()
21
-
22
- private readonly _connections: Map<EdgeId, LikeC4DiagramModel.Connection>
23
-
24
- constructor(
25
- public readonly view: DiagramView,
26
- public readonly model: LikeC4Model.Layouted
27
- ) {
28
- for (const node of view.nodes) {
29
- const el = new LikeC4DiagramModel.Element(node, this)
30
- this._elements.set(node.id, el)
31
- if (isNullish(node.parent)) {
32
- this._rootElements.add(el)
33
- }
34
- }
35
- this._connections = new Map(view.edges.map(e => [e.id, new LikeC4DiagramModel.Connection(e, this)]))
36
- }
37
-
38
- get isDynamic() {
39
- return this.view.__ === 'dynamic'
40
- }
41
-
42
- get id() {
43
- return this.view.id
44
- }
45
-
46
- get title() {
47
- return this.view.title
48
- }
49
-
50
- get viewOf() {
51
- if (isNullish(this.view.__) || this.view.__ === 'element') {
52
- const computedView = this.view as ComputedElementView
53
- return computedView.viewOf ? this.model.element(computedView.viewOf) : null
54
- }
55
- return null
56
- }
57
-
58
- get tags(): C4Tag[] {
59
- return this.view.tags ?? []
60
- }
61
-
62
- public roots(): ReadonlyArray<LikeC4DiagramModel.Element> {
63
- return [...this._rootElements]
64
- }
65
-
66
- public elements(): ReadonlyArray<LikeC4DiagramModel.Element> {
67
- return [...this._elements.values()]
68
- }
69
-
70
- public element(id: Fqn): LikeC4DiagramModel.Element {
71
- return nonNullable(this._elements.get(id), `LikeC4DiagramModel.Element ${id} in view ${this.view.id} not found`)
72
- }
73
-
74
- public hasElement(id: Fqn): boolean {
75
- return this._elements.has(id)
76
- }
77
-
78
- public connections(): ReadonlyArray<LikeC4DiagramModel.Connection> {
79
- return [...this._connections.values()]
80
- }
81
-
82
- public connection(id: EdgeId): LikeC4DiagramModel.Connection {
83
- return nonNullable(this._connections.get(id), `Connection ${id} in view ${this.view.id} not found`)
84
- }
85
-
86
- public findConnections(
87
- source: ElementOrFqn,
88
- target: ElementOrFqn,
89
- direction: 'both' | 'direct' = 'direct'
90
- ): ReadonlyArray<LikeC4DiagramModel.Connection> {
91
- const sourceId = getId(source)
92
- const targetId = getId(target)
93
- return this.connections().filter(c =>
94
- (c.source.id === sourceId && c.target.id === targetId)
95
- || (direction === 'both' && c.source.id === targetId && c.target.id === sourceId)
96
- )
97
- }
98
-
99
- public parent(element: ElementOrFqn): LikeC4DiagramModel.Element | null {
100
- const el = this.element(getId(element))
101
- return el.node.parent ? this.element(el.node.parent) : null
102
- }
103
-
104
- public children(element: ElementOrFqn): ReadonlyArray<LikeC4DiagramModel.Element> {
105
- const el = this.element(getId(element))
106
- return el.node.children.map(c => this.element(c))
107
- }
108
-
109
- // Get all sibling (i.e. same parent)
110
- public siblings(element: ElementOrFqn): ReadonlyArray<LikeC4DiagramModel.Element> {
111
- const id = getId(element)
112
- const parent = this.parent(id)
113
- const siblings = parent ? this.children(parent) : this.roots()
114
- return siblings.filter(e => e.id !== id)
115
- }
116
-
117
- /**
118
- * Get all ancestor elements (i.e. parent, parent’s parent, etc.)
119
- * (from closest to root)
120
- */
121
- public ancestors(element: ElementOrFqn): ReadonlyArray<LikeC4DiagramModel.Element> {
122
- let id = getId(element)
123
- const result = [] as LikeC4DiagramModel.Element[]
124
- let parent
125
- while (parent = this.parent(id)) {
126
- result.push(parent)
127
- id = parent.id
128
- }
129
- return result
130
- }
131
-
132
- public descendants(element: ElementOrFqn): ReadonlyArray<LikeC4DiagramModel.Element> {
133
- const children = this.children(element)
134
- return children.flatMap(c => [c, ...this.descendants(c.id)])
135
- }
136
-
137
- public incoming(
138
- element: ElementOrFqn,
139
- filter: IncomingFilter = 'all'
140
- ): ReadonlyArray<LikeC4DiagramModel.Connection> {
141
- const el = this.element(getId(element))
142
- const edges = el.node.inEdges.map(e => this.connection(e))
143
-
144
- if (filter === 'all' || edges.length === 0) {
145
- return edges
146
- }
147
-
148
- return edges.filter(rel => {
149
- if (filter === 'direct') {
150
- return rel.target.id === el.id
151
- }
152
- return rel.target.id !== el.id
153
- })
154
- }
155
-
156
- public incomers(
157
- element: ElementOrFqn,
158
- filter: IncomingFilter = 'all'
159
- ): ReadonlyArray<LikeC4DiagramModel.Element> {
160
- return this.incoming(element, filter).map(r => r.source)
161
- }
162
-
163
- /**
164
- * Outgoing relationships from the element and its descendants
165
- */
166
- public outgoing(
167
- element: ElementOrFqn,
168
- filter: OutgoingFilter = 'all'
169
- ): ReadonlyArray<LikeC4DiagramModel.Connection> {
170
- const el = this.element(getId(element))
171
- const edges = el.node.outEdges.map(e => this.connection(e))
172
-
173
- if (filter === 'all' || edges.length === 0) {
174
- return edges
175
- }
176
-
177
- return edges.filter(rel => {
178
- if (filter === 'direct') {
179
- return rel.source.id === el.id
180
- }
181
- return rel.source.id !== el.id
182
- })
183
- }
184
-
185
- public outgoers(
186
- element: ElementOrFqn,
187
- filter: OutgoingFilter = 'all'
188
- ): ReadonlyArray<LikeC4DiagramModel.Element> {
189
- return this.outgoing(element, filter).map(r => r.target)
190
- }
191
- }
192
-
193
- export namespace LikeC4DiagramModel {
194
- /**
195
- * Represents an element in the view. (Diagram node)
196
- * All methods are view-scoped, i.e. `children` returns only children of the element in the view.
197
- */
198
- export class Element {
199
- constructor(
200
- public readonly node: DiagramNode,
201
- private readonly view: LikeC4DiagramModel
202
- ) {
203
- }
204
-
205
- get id() {
206
- return this.node.id
207
- }
208
-
209
- get title() {
210
- return this.node.title
211
- }
212
-
213
- get kind(): C4ElementKind {
214
- return this.node.kind
215
- }
216
-
217
- get isRoot(): boolean {
218
- return isNullish(this.node.parent)
219
- }
220
-
221
- get hasNested(): boolean {
222
- return this.node.children.length > 0
223
- }
224
-
225
- get shape(): C4ElementShape {
226
- return this.node.shape
227
- }
228
-
229
- get color(): C4Color {
230
- return this.node.color
231
- }
232
-
233
- get tags(): C4Tag[] {
234
- return this.node.tags ?? []
235
- }
236
-
237
- get level(): number {
238
- return this.node.level
239
- }
240
-
241
- get depth(): number {
242
- return this.node.depth ?? 0
243
- }
244
-
245
- public model(): LikeC4Model.ElementModel<LikeC4DiagramModel> {
246
- return this.view.model.element(this.id)
247
- }
248
-
249
- public parent(): LikeC4DiagramModel.Element | null {
250
- return this.node.parent ? this.view.element(this.node.parent) : null
251
- }
252
-
253
- public metadata(key: string): string | undefined
254
- public metadata(key: string, defaultValue: string): string
255
- public metadata(key: string, defaultValue?: string): string | undefined {
256
- return this.model().metadata(key) ?? defaultValue
257
- }
258
-
259
- public hasMetadata(key: string): boolean {
260
- return this.model().hasMetadata(key)
261
- }
262
-
263
- public ancestors(): ReadonlyArray<LikeC4DiagramModel.Element> {
264
- return this.view.ancestors(this)
265
- }
266
-
267
- public siblings(): ReadonlyArray<LikeC4DiagramModel.Element> {
268
- return this.view.siblings(this)
269
- }
270
-
271
- public descendants(): ReadonlyArray<LikeC4DiagramModel.Element> {
272
- return this.view.descendants(this)
273
- }
274
-
275
- public children(): ReadonlyArray<LikeC4DiagramModel.Element> {
276
- return this.view.children(this)
277
- }
278
-
279
- public incoming(filter: IncomingFilter = 'all'): ReadonlyArray<LikeC4DiagramModel.Connection> {
280
- return this.view.incoming(this, filter)
281
- }
282
-
283
- public incomers(filter: IncomingFilter = 'all'): ReadonlyArray<LikeC4DiagramModel.Element> {
284
- return this.view.incomers(this, filter)
285
- }
286
-
287
- public outgoing(filter: OutgoingFilter = 'all'): ReadonlyArray<LikeC4DiagramModel.Connection> {
288
- return this.view.outgoing(this, filter)
289
- }
290
-
291
- public outgoers(filter: OutgoingFilter = 'all'): ReadonlyArray<LikeC4DiagramModel.Element> {
292
- return this.view.outgoers(this, filter)
293
- }
294
-
295
- public connectionsTo(target: Fqn | LikeC4DiagramModel.Element): ReadonlyArray<LikeC4DiagramModel.Connection> {
296
- return this.view.findConnections(this, target)
297
- }
298
- }
299
-
300
- /**
301
- * Represents a connection between two elements.
302
- * May be source from multiple model relationships.
303
- */
304
- export class Connection {
305
- constructor(
306
- public readonly edge: DiagramEdge,
307
- private readonly view: LikeC4DiagramModel
308
- ) {
309
- }
310
-
311
- get id() {
312
- return this.edge.id
313
- }
314
-
315
- get source(): LikeC4DiagramModel.Element {
316
- return this.view.element(this.edge.source)
317
- }
318
-
319
- get target(): LikeC4DiagramModel.Element {
320
- return this.view.element(this.edge.target)
321
- }
322
-
323
- get tags(): C4Tag[] {
324
- return this.edge.tags ?? []
325
- }
326
-
327
- get color(): C4Color {
328
- return this.edge.color ?? DefaultRelationshipColor
329
- }
330
-
331
- /**
332
- * Model relationships
333
- */
334
- relationships(): ReadonlyArray<LikeC4Model.Relationship<LikeC4DiagramModel>> {
335
- return this.edge.relations.map(r => nonNullable(this.view.model.relationship(r)))
336
- }
337
- }
338
- }
@@ -1,320 +0,0 @@
1
- import { isNullish } from 'remeda'
2
- import { nonNullable } from '../errors'
3
- import {
4
- type ElementKind as C4ElementKind,
5
- type ElementShape as C4ElementShape,
6
- type Tag as C4Tag
7
- } from '../types/element'
8
- import type { Color as C4Color } from '../types/theme'
9
- import type { ComputedEdge, ComputedNode, ComputedView } from '../types/view'
10
- import type { LikeC4Model } from './LikeC4Model'
11
- import { type EdgeId, type ElementOrFqn, type Fqn, getId, type IncomingFilter, type OutgoingFilter } from './types'
12
-
13
- /**
14
- * All methods are view-scoped, i.e. only return elements and connections in the view.
15
- */
16
- export class LikeC4ViewModel {
17
- private readonly _rootElements = new Set<LikeC4ViewModel.Element>()
18
-
19
- private readonly _elements = new Map<Fqn, LikeC4ViewModel.Element>()
20
-
21
- private readonly _connections: Map<EdgeId, LikeC4ViewModel.Connection>
22
-
23
- constructor(
24
- public readonly view: ComputedView,
25
- public readonly model: LikeC4Model<LikeC4ViewModel>
26
- ) {
27
- for (const node of view.nodes) {
28
- const el = new LikeC4ViewModel.Element(node, this)
29
- this._elements.set(node.id, el)
30
- if (isNullish(node.parent)) {
31
- this._rootElements.add(el)
32
- }
33
- }
34
- this._connections = new Map(view.edges.map(e => [e.id, new LikeC4ViewModel.Connection(e, this)]))
35
- }
36
-
37
- get id() {
38
- return this.view.id
39
- }
40
-
41
- get title() {
42
- return this.view.title
43
- }
44
-
45
- get viewOf() {
46
- if (this.view.__ !== 'dynamic') {
47
- return this.view.viewOf ? this.model.element(this.view.viewOf) : null
48
- }
49
- return null
50
- }
51
-
52
- get tags(): C4Tag[] {
53
- return this.view.tags ?? []
54
- }
55
-
56
- public roots(): ReadonlyArray<LikeC4ViewModel.Element> {
57
- return [...this._rootElements]
58
- }
59
-
60
- public elements(): ReadonlyArray<LikeC4ViewModel.Element> {
61
- return [...this._elements.values()]
62
- }
63
-
64
- public element(id: Fqn): LikeC4ViewModel.Element {
65
- return nonNullable(this._elements.get(id), `LikeC4ViewModel.Element ${id} in view ${this.id} not found`)
66
- }
67
-
68
- public hasElement(id: Fqn): boolean {
69
- return this._elements.has(id)
70
- }
71
-
72
- public connections(): ReadonlyArray<LikeC4ViewModel.Connection> {
73
- return [...this._connections.values()]
74
- }
75
-
76
- public connection(id: EdgeId): LikeC4ViewModel.Connection {
77
- return nonNullable(this._connections.get(id), `Connection ${id} in view ${this.id} not found`)
78
- }
79
-
80
- public findConnections(
81
- source: ElementOrFqn,
82
- target: ElementOrFqn,
83
- direction: 'both' | 'direct' = 'direct'
84
- ): ReadonlyArray<LikeC4ViewModel.Connection> {
85
- const sourceId = getId(source)
86
- const targetId = getId(target)
87
- return this.connections().filter(c =>
88
- (c.source.id === sourceId && c.target.id === targetId)
89
- || (direction === 'both' && c.source.id === targetId && c.target.id === sourceId)
90
- )
91
- }
92
-
93
- public parent(element: ElementOrFqn): LikeC4ViewModel.Element | null {
94
- const el = this.element(getId(element))
95
- return el.node.parent ? this.element(el.node.parent) : null
96
- }
97
-
98
- public children(element: ElementOrFqn): ReadonlyArray<LikeC4ViewModel.Element> {
99
- const el = this.element(getId(element))
100
- return el.node.children.map(c => this.element(c))
101
- }
102
-
103
- // Get all sibling (i.e. same parent)
104
- public siblings(element: ElementOrFqn): ReadonlyArray<LikeC4ViewModel.Element> {
105
- const id = getId(element)
106
- const parent = this.parent(id)
107
- const siblings = parent ? this.children(parent) : this.roots()
108
- return siblings.filter(e => e.id !== id)
109
- }
110
-
111
- /**
112
- * Get all ancestor elements (i.e. parent, parent’s parent, etc.)
113
- * (from closest to root)
114
- */
115
- public ancestors(element: ElementOrFqn): ReadonlyArray<LikeC4ViewModel.Element> {
116
- let id = getId(element)
117
- const result = [] as LikeC4ViewModel.Element[]
118
- let parent
119
- while (parent = this.parent(id)) {
120
- result.push(parent)
121
- id = parent.id
122
- }
123
- return result
124
- }
125
-
126
- public descendants(element: ElementOrFqn): ReadonlyArray<LikeC4ViewModel.Element> {
127
- const children = this.children(element)
128
- return children.flatMap(c => [c, ...this.descendants(c.id)])
129
- }
130
-
131
- public incoming(
132
- element: ElementOrFqn,
133
- filter: IncomingFilter = 'all'
134
- ): ReadonlyArray<LikeC4ViewModel.Connection> {
135
- const el = this.element(getId(element))
136
- const edges = el.node.inEdges.map(e => nonNullable(this._connections.get(e), `Edge ${e} not found`))
137
-
138
- if (filter === 'all' || edges.length === 0) {
139
- return edges
140
- }
141
-
142
- return edges.filter(rel => {
143
- if (filter === 'direct') {
144
- return rel.target.id === el.id
145
- }
146
- return rel.target.id !== el.id
147
- })
148
- }
149
-
150
- public incomers(
151
- element: ElementOrFqn,
152
- filter: IncomingFilter = 'all'
153
- ): ReadonlyArray<LikeC4ViewModel.Element> {
154
- return [...new Set(this.incoming(element, filter).map(r => r.source))]
155
- }
156
-
157
- /**
158
- * Outgoing relationships from the element and its descendants
159
- */
160
- public outgoing(
161
- element: ElementOrFqn,
162
- filter: OutgoingFilter = 'all'
163
- ): ReadonlyArray<LikeC4ViewModel.Connection> {
164
- const el = this.element(getId(element))
165
- const edges = el.node.outEdges.map(e => nonNullable(this._connections.get(e), `Edge ${e} not found`))
166
-
167
- if (filter === 'all' || edges.length === 0) {
168
- return edges
169
- }
170
-
171
- return edges.filter(rel => {
172
- if (filter === 'direct') {
173
- return rel.source.id === el.id
174
- }
175
- return rel.source.id !== el.id
176
- })
177
- }
178
-
179
- public outgoers(
180
- element: ElementOrFqn,
181
- filter: OutgoingFilter = 'all'
182
- ): ReadonlyArray<LikeC4ViewModel.Element> {
183
- return [...new Set(this.outgoing(element, filter).map(r => r.target))]
184
- }
185
- }
186
-
187
- export namespace LikeC4ViewModel {
188
- /**
189
- * Represents an element in the view. (Diagram node)
190
- * All methods are view-scoped, i.e. `children` returns only children of the element in the view.
191
- */
192
- export class Element {
193
- constructor(
194
- public readonly node: ComputedNode,
195
- private readonly viewmodel: LikeC4ViewModel
196
- ) {
197
- }
198
-
199
- get id() {
200
- return this.node.id
201
- }
202
-
203
- get title() {
204
- return this.node.title
205
- }
206
-
207
- get kind(): C4ElementKind {
208
- return this.node.kind
209
- }
210
-
211
- get isRoot(): boolean {
212
- return isNullish(this.node.parent)
213
- }
214
-
215
- get hasNested(): boolean {
216
- return this.node.children.length > 0
217
- }
218
-
219
- get shape(): C4ElementShape {
220
- return this.node.shape
221
- }
222
-
223
- get color(): C4Color {
224
- return this.node.color
225
- }
226
-
227
- get tags(): C4Tag[] {
228
- return this.node.tags ?? []
229
- }
230
-
231
- public model(): LikeC4Model.ElementModel<LikeC4ViewModel> {
232
- return this.viewmodel.model.element(this.id)
233
- }
234
-
235
- public parent(): LikeC4ViewModel.Element | null {
236
- return this.node.parent ? this.viewmodel.element(this.node.parent) : null
237
- }
238
-
239
- public metadata(key: string): string | undefined
240
- public metadata(key: string, defaultValue: string): string
241
- public metadata(key: string, defaultValue?: string): string | undefined {
242
- return this.model().metadata(key) ?? defaultValue
243
- }
244
-
245
- public hasMetadata(key: string): boolean {
246
- return this.model().hasMetadata(key)
247
- }
248
-
249
- public ancestors(): ReadonlyArray<LikeC4ViewModel.Element> {
250
- return this.viewmodel.ancestors(this)
251
- }
252
-
253
- public siblings(): ReadonlyArray<LikeC4ViewModel.Element> {
254
- return this.viewmodel.siblings(this)
255
- }
256
-
257
- public descendants(): ReadonlyArray<LikeC4ViewModel.Element> {
258
- return this.viewmodel.descendants(this)
259
- }
260
-
261
- public children(): ReadonlyArray<LikeC4ViewModel.Element> {
262
- return this.viewmodel.children(this)
263
- }
264
-
265
- public incoming(filter: 'all' | 'direct' | 'to-descendants' = 'all'): ReadonlyArray<LikeC4ViewModel.Connection> {
266
- return this.viewmodel.incoming(this, filter)
267
- }
268
-
269
- public incomers(filter: 'all' | 'direct' | 'to-descendants' = 'all'): ReadonlyArray<LikeC4ViewModel.Element> {
270
- return this.viewmodel.incomers(this, filter)
271
- }
272
-
273
- public outgoing(filter: OutgoingFilter = 'all'): ReadonlyArray<LikeC4ViewModel.Connection> {
274
- return this.viewmodel.outgoing(this, filter)
275
- }
276
-
277
- public outgoers(filter: OutgoingFilter = 'all'): ReadonlyArray<LikeC4ViewModel.Element> {
278
- return this.viewmodel.outgoers(this, filter)
279
- }
280
-
281
- public connectionsTo(target: Fqn | LikeC4ViewModel.Element): ReadonlyArray<LikeC4ViewModel.Connection> {
282
- return this.viewmodel.findConnections(this, target)
283
- }
284
- }
285
-
286
- /**
287
- * Represents a connection between two elements.
288
- * May be source from multiple model relationships.
289
- */
290
- export class Connection {
291
- constructor(
292
- public readonly edge: ComputedEdge,
293
- private readonly viewmodel: LikeC4ViewModel
294
- ) {
295
- }
296
-
297
- get id() {
298
- return this.edge.id
299
- }
300
-
301
- get source(): LikeC4ViewModel.Element {
302
- return this.viewmodel.element(this.edge.source)
303
- }
304
-
305
- get target(): LikeC4ViewModel.Element {
306
- return this.viewmodel.element(this.edge.target)
307
- }
308
-
309
- get tags(): C4Tag[] {
310
- return this.edge.tags ?? []
311
- }
312
-
313
- /**
314
- * Model relationships
315
- */
316
- relationships(): ReadonlyArray<LikeC4Model.Relationship<LikeC4ViewModel>> {
317
- return this.edge.relations.map(r => nonNullable(this.viewmodel.model.relationship(r)))
318
- }
319
- }
320
- }