@likec4/core 1.30.0 → 1.32.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 (186) hide show
  1. package/README.md +407 -5
  2. package/dist/builder/index.d.mts +221 -42
  3. package/dist/builder/index.mjs +118 -65
  4. package/dist/compute-view/index.d.mts +23 -7
  5. package/dist/compute-view/index.mjs +11 -6
  6. package/dist/compute-view/relationships-view/index.d.mts +9 -7
  7. package/dist/compute-view/relationships-view/index.mjs +3 -2
  8. package/dist/index.d.mts +10 -14
  9. package/dist/index.mjs +10 -6
  10. package/dist/model/index.d.mts +173 -19
  11. package/dist/model/index.mjs +6 -1
  12. package/dist/shared/core.BaYE_r29.d.mts +258 -0
  13. package/dist/shared/core.Bk7wEZFG.mjs +276 -0
  14. package/dist/shared/core.BrCnZFTn.mjs +32 -0
  15. package/dist/shared/core.BuO4ncfY.d.mts +146 -0
  16. package/dist/shared/{core.CAcZ6_cP.mjs → core.CBpKAlhj.mjs} +1742 -4128
  17. package/dist/shared/core.CCWXGc6G.d.mts +920 -0
  18. package/dist/shared/core.CUYDgv2-.mjs +214 -0
  19. package/dist/shared/{core.Cu-UdkSl.mjs → core.CpXlRyE5.mjs} +7 -494
  20. package/dist/shared/{core.C-YXI-43.mjs → core.Cy9smucx.mjs} +3 -1
  21. package/dist/shared/core.CywrQs86.d.mts +31 -0
  22. package/dist/shared/core.DoK86JEe.mjs +518 -0
  23. package/dist/shared/core.Dpq3P3YJ.d.mts +1265 -0
  24. package/dist/shared/{core.D2830qgg.d.mts → core.Pf5I9o0e.d.mts} +1 -1
  25. package/dist/shared/core.cbA9VZ02.mjs +2681 -0
  26. package/dist/shared/{core.CQXU9DF7.mjs → core.rmvsy0n3.mjs} +23 -32
  27. package/dist/types/aux.d.mts +3 -0
  28. package/dist/types/aux.mjs +1 -0
  29. package/dist/types/index.d.mts +7 -45
  30. package/dist/types/index.mjs +130 -3
  31. package/dist/utils/index.d.mts +221 -53
  32. package/dist/utils/index.mjs +14 -10
  33. package/package.json +19 -10
  34. package/src/builder/Builder.deploymentModel.ts +82 -18
  35. package/src/builder/Builder.element.ts +40 -20
  36. package/src/builder/Builder.model.ts +66 -17
  37. package/src/builder/Builder.ts +168 -76
  38. package/src/builder/Builder.view-common.ts +35 -17
  39. package/src/builder/Builder.view-deployment.ts +25 -25
  40. package/src/builder/Builder.view-element.ts +13 -12
  41. package/src/builder/Builder.views.ts +12 -7
  42. package/src/builder/__snapshots__/Builder-style1.spec.ts.snap +62 -34
  43. package/src/builder/__snapshots__/Builder-style2.compute-model.json5 +319 -0
  44. package/src/builder/__snapshots__/Builder-style2.spec.ts.snap +45 -24
  45. package/src/builder/_types.ts +34 -27
  46. package/src/compute-view/compute-view.ts +31 -34
  47. package/src/compute-view/deployment-view/__test__/TestHelper.ts +27 -14
  48. package/src/compute-view/deployment-view/__test__/fixture.ts +15 -13
  49. package/src/compute-view/deployment-view/_types.ts +10 -10
  50. package/src/compute-view/deployment-view/clean-connections.ts +5 -7
  51. package/src/compute-view/deployment-view/compute.ts +16 -13
  52. package/src/compute-view/deployment-view/memory/memory.ts +5 -6
  53. package/src/compute-view/deployment-view/predicates/deploymentRefs.ts +4 -5
  54. package/src/compute-view/deployment-view/predicates/relation-direct.ts +25 -50
  55. package/src/compute-view/deployment-view/predicates/relation-in-out.ts +5 -7
  56. package/src/compute-view/deployment-view/predicates/relation-incoming.ts +7 -11
  57. package/src/compute-view/deployment-view/predicates/relation-outgoing.ts +7 -10
  58. package/src/compute-view/deployment-view/predicates/utils.ts +37 -31
  59. package/src/compute-view/deployment-view/predicates/wildcard.ts +3 -2
  60. package/src/compute-view/deployment-view/stages/stage-final.ts +2 -2
  61. package/src/compute-view/deployment-view/stages/stage-include.ts +1 -1
  62. package/src/compute-view/deployment-view/utils.ts +79 -72
  63. package/src/compute-view/dynamic-view/__test__/fixture.ts +20 -11
  64. package/src/compute-view/dynamic-view/compute.ts +60 -53
  65. package/src/compute-view/element-view/__test__/TestHelper.ts +53 -29
  66. package/src/compute-view/element-view/__test__/__snapshots__/legacy.spec.ts.snap +26 -26
  67. package/src/compute-view/element-view/__test__/fixture.ts +134 -83
  68. package/src/compute-view/element-view/_types.ts +14 -19
  69. package/src/compute-view/element-view/clean-connections.ts +12 -8
  70. package/src/compute-view/element-view/compute.ts +61 -51
  71. package/src/compute-view/element-view/memory/NodeGroup.ts +8 -9
  72. package/src/compute-view/element-view/memory/memory.ts +22 -22
  73. package/src/compute-view/element-view/memory/stage-include.ts +5 -3
  74. package/src/compute-view/element-view/predicates/_utils.ts +18 -14
  75. package/src/compute-view/element-view/predicates/element-expand.ts +4 -5
  76. package/src/compute-view/element-view/predicates/element-kind-tag.ts +3 -4
  77. package/src/compute-view/element-view/predicates/element-ref.ts +3 -4
  78. package/src/compute-view/element-view/predicates/relation-direct.ts +16 -12
  79. package/src/compute-view/element-view/predicates/relation-in-out.ts +7 -8
  80. package/src/compute-view/element-view/predicates/relation-in.ts +19 -26
  81. package/src/compute-view/element-view/predicates/relation-out.ts +20 -21
  82. package/src/compute-view/element-view/predicates/wildcard.ts +3 -3
  83. package/src/compute-view/element-view/utils.ts +25 -23
  84. package/src/compute-view/index.ts +1 -1
  85. package/src/compute-view/memory/AbstractStageExclude.ts +3 -4
  86. package/src/compute-view/memory/AbstractStageInclude.ts +1 -1
  87. package/src/compute-view/memory/_types.ts +3 -2
  88. package/src/compute-view/relationships-view/_types.ts +3 -4
  89. package/src/compute-view/relationships-view/compute.ts +8 -11
  90. package/src/compute-view/relationships-view/layout.ts +6 -6
  91. package/src/compute-view/relationships-view/utils.ts +4 -4
  92. package/src/compute-view/utils/ancestorsOfNode.ts +7 -7
  93. package/src/compute-view/utils/applyCustomElementProperties.ts +16 -8
  94. package/src/compute-view/utils/applyCustomRelationProperties.ts +19 -9
  95. package/src/compute-view/utils/applyViewRuleStyles.ts +18 -10
  96. package/src/compute-view/utils/buildComputedNodes.ts +40 -40
  97. package/src/compute-view/utils/buildElementNotations.ts +14 -14
  98. package/src/compute-view/utils/elementExpressionToPredicate.ts +14 -15
  99. package/src/compute-view/utils/link-nodes-with-edges.ts +6 -4
  100. package/src/compute-view/utils/merge-props-from-relationships.ts +52 -20
  101. package/src/compute-view/utils/relationExpressionToPredicates.ts +19 -19
  102. package/src/compute-view/utils/resolve-extended-views.ts +8 -7
  103. package/src/compute-view/utils/resolve-global-rules.ts +31 -26
  104. package/src/compute-view/utils/topological-sort.ts +14 -14
  105. package/src/compute-view/utils/view-hash.ts +2 -2
  106. package/src/compute-view/utils/with-readable-edges.ts +9 -5
  107. package/src/index.ts +0 -32
  108. package/src/model/DeploymentElementModel.ts +182 -137
  109. package/src/model/DeploymentModel.ts +88 -65
  110. package/src/model/ElementModel.ts +92 -72
  111. package/src/model/LikeC4Model.ts +430 -241
  112. package/src/model/RelationModel.ts +69 -26
  113. package/src/model/__test__/fixture.ts +50 -36
  114. package/src/model/connection/Connection.ts +2 -2
  115. package/src/model/connection/deployment/DeploymentConnectionModel.ts +21 -24
  116. package/src/model/connection/deployment/find.ts +1 -1
  117. package/src/model/connection/model/ConnectionModel.ts +21 -20
  118. package/src/model/connection/model/find.ts +7 -7
  119. package/src/model/connection/ops.ts +6 -6
  120. package/src/model/guards.ts +66 -14
  121. package/src/model/index.ts +20 -7
  122. package/src/model/types.ts +41 -97
  123. package/src/model/view/EdgeModel.ts +47 -41
  124. package/src/model/view/LikeC4ViewModel.ts +130 -80
  125. package/src/model/view/NodeModel.ts +102 -96
  126. package/src/theme/element.ts +1 -1
  127. package/src/theme/index.ts +9 -9
  128. package/src/theme/relationships.ts +1 -1
  129. package/src/types/_common.ts +21 -15
  130. package/src/types/aux.ts +285 -0
  131. package/src/types/const.ts +34 -0
  132. package/src/types/expression-model.ts +292 -0
  133. package/src/types/expression.ts +311 -0
  134. package/src/types/fqnRef.ts +88 -0
  135. package/src/types/geometry.ts +131 -0
  136. package/src/types/global.ts +10 -8
  137. package/src/types/index.ts +39 -201
  138. package/src/types/model-data.ts +52 -84
  139. package/src/types/model-deployment.ts +87 -0
  140. package/src/types/model-dump.ts +72 -0
  141. package/src/types/model-logical.ts +111 -0
  142. package/src/types/model-spec.ts +81 -0
  143. package/src/types/operators.ts +51 -52
  144. package/src/types/scalar.ts +114 -0
  145. package/src/types/styles.ts +123 -0
  146. package/src/types/view-changes.ts +4 -5
  147. package/src/types/view-common.ts +157 -0
  148. package/src/types/view-computed.ts +113 -0
  149. package/src/types/view-layouted.ts +82 -0
  150. package/src/types/view-parsed.deployment.ts +40 -0
  151. package/src/types/view-parsed.dynamic.ts +78 -0
  152. package/src/types/view-parsed.element.ts +65 -0
  153. package/src/types/view.ts +122 -542
  154. package/src/utils/compare-natural.ts +43 -1
  155. package/src/utils/getOrCreate.ts +0 -34
  156. package/src/utils/index.ts +7 -12
  157. package/src/utils/iterable/filter.ts +1 -1
  158. package/src/utils/iterable/find.ts +3 -1
  159. package/src/utils/iterable/head.ts +22 -0
  160. package/src/utils/iterable/index.ts +1 -0
  161. package/src/utils/iterable/map.ts +1 -1
  162. package/src/utils/iterable/reduce.ts +1 -1
  163. package/src/utils/iterable/some.ts +1 -1
  164. package/src/utils/memoize-prop.ts +30 -0
  165. package/src/utils/object-hash.ts +1 -1
  166. package/src/utils/relations.ts +14 -11
  167. package/src/utils/string-hash.ts +1 -1
  168. package/dist/shared/core.CZR9J1Qx.d.mts +0 -146
  169. package/dist/shared/core.CiFKryGW.d.mts +0 -745
  170. package/dist/shared/core.DJvmfnEi.d.mts +0 -1153
  171. package/dist/shared/core.DgfwjBtu.mjs +0 -495
  172. package/dist/shared/core.SjLhMA7a.d.mts +0 -62
  173. package/src/builder/__snapshots__/Builder.spec.ts.snap +0 -374
  174. package/src/compute-view/element-view/predicates.ts +0 -520
  175. package/src/compute-view/utils/uniqueTags.ts +0 -21
  176. package/src/types/deployments.ts +0 -170
  177. package/src/types/element.ts +0 -109
  178. package/src/types/expression-v2-model.ts +0 -314
  179. package/src/types/expression-v2.ts +0 -392
  180. package/src/types/overview-graph.ts +0 -43
  181. package/src/types/relation.ts +0 -61
  182. package/src/types/scalars.ts +0 -39
  183. package/src/types/theme.ts +0 -82
  184. package/src/types/view-notation.ts +0 -9
  185. package/src/utils/graphlib.ts +0 -10
  186. /package/src/{errors/index.ts → utils/invariant.ts} +0 -0
@@ -1,45 +1,52 @@
1
- import { nonNullable } from '../../errors'
2
- import type { IteratorLike } from '../../types/_common'
3
- import type { Link } from '../../types/element'
4
- import type { Tag } from '../../types/scalars'
5
- import {
6
- type ComputedDeploymentView,
7
- type ComputedDynamicView,
8
- type ComputedElementView,
9
- type ComputedView,
10
- type DiagramView,
11
- type EdgeId as C4EdgeId,
12
- type LikeC4View,
13
- type NodeId as C4NodeId,
14
- isDeploymentView,
15
- isDynamicView,
16
- isElementView,
17
- isScopedElementView,
18
- } from '../../types/view'
19
- import { DefaultMap, ifind } from '../../utils'
1
+ import { isTruthy } from 'remeda'
2
+ import type {
3
+ Any,
4
+ AnyView,
5
+ IteratorLike,
6
+ Link,
7
+ scalar,
8
+ ViewWithType,
9
+ } from '../../types'
10
+ import { _stage, _type } from '../../types'
11
+ import type * as aux from '../../types/aux'
12
+ import { DefaultMap, ifind, nonNullable } from '../../utils'
20
13
  import type { ElementModel } from '../ElementModel'
21
14
  import type { LikeC4Model } from '../LikeC4Model'
22
- import { type AnyAux, getId } from '../types'
15
+ import {
16
+ type $View,
17
+ type EdgeOrId,
18
+ type NodeOrId,
19
+ getId,
20
+ } from '../types'
23
21
  import { type EdgesIterator, EdgeModel } from './EdgeModel'
24
22
  import { type NodesIterator, NodeModel } from './NodeModel'
25
23
 
26
- export type ViewsIterator<M extends AnyAux> = IteratorLike<LikeC4ViewModel<M>>
27
-
28
- export class LikeC4ViewModel<M extends AnyAux, V extends ComputedView | DiagramView = M['ViewType']> {
29
- readonly #rootnodes = new Set<NodeModel<M, V>>()
30
- readonly #nodes = new Map<C4NodeId, NodeModel<M, V>>()
31
- readonly #edges = new Map<C4EdgeId, EdgeModel<M, V>>()
32
- readonly #includeElements = new Set<M['Element']>()
33
- readonly #includeDeployments = new Set<M['Deployment']>()
34
- readonly #includeRelations = new Set<M['RelationId']>()
35
- readonly #allTags = new DefaultMap((_key: Tag) => new Set<NodeModel<M, V> | EdgeModel<M, V>>())
36
-
37
- constructor(
38
- public readonly $model: LikeC4Model<M>,
39
- public readonly $view: V,
40
- ) {
41
- for (const node of $view.nodes) {
42
- const el = new NodeModel(this, Object.freeze(node))
24
+ export type ViewsIterator<A extends Any, V extends $View<A> = $View<A>> = IteratorLike<LikeC4ViewModel<A, V>>
25
+
26
+ export type InferViewType<V> = V extends AnyView<any> ? V[_type] : never
27
+
28
+ export class LikeC4ViewModel<A extends Any = Any, V extends $View<A> = $View<A>> {
29
+ /**
30
+ * Don't use in runtime, only for type inference
31
+ */
32
+ readonly Aux!: A
33
+
34
+ readonly #rootnodes = new Set<NodeModel<A, V>>()
35
+ readonly #nodes = new Map<scalar.NodeId, NodeModel<A, V>>()
36
+ readonly #edges = new Map<scalar.EdgeId, EdgeModel<A, V>>()
37
+ readonly #includeElements = new Set<aux.Fqn<A>>()
38
+ readonly #includeDeployments = new Set<aux.DeploymentFqn<A>>()
39
+ readonly #includeRelations = new Set<scalar.RelationId>()
40
+ readonly #allTags = new DefaultMap((_key: aux.Tag<A>) => new Set<NodeModel<A, V> | EdgeModel<A, V>>())
41
+
42
+ public readonly $view: V
43
+ public readonly $model: LikeC4Model<A>
44
+
45
+ constructor(model: LikeC4Model<A>, view: V) {
46
+ this.$model = model
47
+ this.$view = view
48
+ for (const node of view.nodes) {
49
+ const el = new NodeModel<A, V>(this, Object.freeze(node))
43
50
  this.#nodes.set(node.id, el)
44
51
  if (!node.parent) {
45
52
  this.#rootnodes.add(el)
@@ -55,7 +62,7 @@ export class LikeC4ViewModel<M extends AnyAux, V extends ComputedView | DiagramV
55
62
  }
56
63
  }
57
64
 
58
- for (const edge of $view.edges) {
65
+ for (const edge of view.edges) {
59
66
  const edgeModel = new EdgeModel(
60
67
  this,
61
68
  Object.freeze(edge),
@@ -72,11 +79,11 @@ export class LikeC4ViewModel<M extends AnyAux, V extends ComputedView | DiagramV
72
79
  }
73
80
  }
74
81
 
75
- get __(): NonNullable<M['ViewType']['__']> {
76
- return this.$view.__ ?? 'element'
82
+ get _type(): V[_type] {
83
+ return this.$view[_type]
77
84
  }
78
85
 
79
- get id(): M['ViewId'] {
86
+ get id(): aux.StrictViewId<A> {
80
87
  return this.$view.id
81
88
  }
82
89
 
@@ -84,7 +91,7 @@ export class LikeC4ViewModel<M extends AnyAux, V extends ComputedView | DiagramV
84
91
  return this.$view.title
85
92
  }
86
93
 
87
- get tags(): ReadonlyArray<Tag> {
94
+ get tags(): aux.Tags<A> {
88
95
  return this.$view.tags ?? []
89
96
  }
90
97
 
@@ -92,10 +99,10 @@ export class LikeC4ViewModel<M extends AnyAux, V extends ComputedView | DiagramV
92
99
  return this.$view.links ?? []
93
100
  }
94
101
 
95
- get viewOf(): ElementModel<M> | null {
96
- const v = this.$view as LikeC4View
97
- if (isScopedElementView(v)) {
98
- return this.$model.element(v.viewOf)
102
+ get viewOf(): ElementModel<A> | null {
103
+ if (this.isElementView()) {
104
+ const viewOf = this.$view.viewOf
105
+ return viewOf ? this.$model.element(viewOf) : null
99
106
  }
100
107
  return null
101
108
  }
@@ -103,18 +110,18 @@ export class LikeC4ViewModel<M extends AnyAux, V extends ComputedView | DiagramV
103
110
  /**
104
111
  * All tags from nodes and edges.
105
112
  */
106
- get includedTags(): ReadonlyArray<Tag> {
107
- return [...this.#allTags.keys()]
113
+ get includedTags(): aux.Tags<A> {
114
+ return [...this.#allTags.keys()] as unknown as aux.Tags<A>
108
115
  }
109
116
 
110
- public roots(): NodesIterator<M, V> {
117
+ public roots(): NodesIterator<A, V> {
111
118
  return this.#rootnodes.values()
112
119
  }
113
120
 
114
121
  /**
115
122
  * Iterate over all nodes that have children.
116
123
  */
117
- public *compounds(): NodesIterator<M, V> {
124
+ public *compounds(): NodesIterator<A, V> {
118
125
  for (const node of this.#nodes.values()) {
119
126
  if (node.hasChildren()) {
120
127
  yield node
@@ -127,7 +134,7 @@ export class LikeC4ViewModel<M extends AnyAux, V extends ComputedView | DiagramV
127
134
  * Get node by id.
128
135
  * @throws Error if node is not found.
129
136
  */
130
- public node(node: M['NodeOrId']): NodeModel<M, V> {
137
+ public node(node: NodeOrId): NodeModel<A, V> {
131
138
  const nodeId = getId(node)
132
139
  return nonNullable(this.#nodes.get(nodeId), `Node ${nodeId} not found in view ${this.$view.id}`)
133
140
  }
@@ -135,19 +142,22 @@ export class LikeC4ViewModel<M extends AnyAux, V extends ComputedView | DiagramV
135
142
  /**
136
143
  * Find node by id.
137
144
  */
138
- public findNode(node: M['NodeOrId']): NodeModel<M, V> | null {
139
- return this.#nodes.get(getId(node) as C4NodeId) ?? null
145
+ public findNode(node: NodeOrId): NodeModel<A, V> | null {
146
+ return this.#nodes.get(getId(node)) ?? null
140
147
  }
141
148
 
142
- public findNodeWithElement(fqn: M['Element']): NodeModel.WithElement<M, V> | null {
143
- const nd = ifind(this.#nodes.values(), node => node.element?.id === fqn) ?? null
144
- return nd && nd.hasElement() ? nd : null
149
+ public findNodeWithElement(element: aux.LooseElementId<A> | { id: aux.Fqn<A> }): NodeModel.WithElement<A, V> | null {
150
+ const id = getId(element)
151
+ return ifind(
152
+ this.#nodes.values(),
153
+ (node): node is NodeModel.WithElement<A, V> => node.hasElement() && node.element.id === id,
154
+ ) ?? null
145
155
  }
146
156
 
147
157
  /**
148
158
  * Iterate over all nodes.
149
159
  */
150
- public nodes(): NodesIterator<M, V> {
160
+ public nodes(): NodesIterator<A, V> {
151
161
  return this.#nodes.values()
152
162
  }
153
163
 
@@ -156,24 +166,24 @@ export class LikeC4ViewModel<M extends AnyAux, V extends ComputedView | DiagramV
156
166
  * @param edge Edge or id
157
167
  * @returns EdgeModel
158
168
  */
159
- public edge(edge: M['EdgeOrId']): EdgeModel<M, V> {
160
- const edgeId = getId(edge) as C4EdgeId
169
+ public edge(edge: EdgeOrId): EdgeModel<A, V> {
170
+ const edgeId = getId(edge)
161
171
  return nonNullable(this.#edges.get(edgeId), `Edge ${edgeId} not found in view ${this.$view.id}`)
162
172
  }
163
- public findEdge(edge: M['EdgeOrId']): EdgeModel<M, V> | null {
164
- return this.#edges.get(getId(edge) as C4EdgeId) ?? null
173
+ public findEdge(edge: EdgeOrId): EdgeModel<A, V> | null {
174
+ return this.#edges.get(getId(edge)) ?? null
165
175
  }
166
176
  /**
167
177
  * Iterate over all edges.
168
178
  */
169
- public edges(): EdgesIterator<M, V> {
179
+ public edges(): EdgesIterator<A, V> {
170
180
  return this.#edges.values()
171
181
  }
172
182
 
173
183
  /**
174
184
  * Iterate over all edges.
175
185
  */
176
- public *edgesWithRelation(relation: M['RelationId']): EdgesIterator<M, V> {
186
+ public *edgesWithRelation(relation: aux.RelationId): EdgesIterator<A, V> {
177
187
  for (const edge of this.#edges.values()) {
178
188
  if (edge.includesRelation(relation)) {
179
189
  yield edge
@@ -185,7 +195,7 @@ export class LikeC4ViewModel<M extends AnyAux, V extends ComputedView | DiagramV
185
195
  /**
186
196
  * Nodes that have references to elements from logical model.
187
197
  */
188
- public *elements(): IteratorLike<NodeModel.WithElement<M, V>> {
198
+ public *elements(): IteratorLike<NodeModel.WithElement<A, V>> {
189
199
  // return this.#nodes.values().filter(node => node.hasElement())
190
200
  for (const node of this.#nodes.values()) {
191
201
  if (node.hasElement()) {
@@ -195,38 +205,78 @@ export class LikeC4ViewModel<M extends AnyAux, V extends ComputedView | DiagramV
195
205
  return
196
206
  }
197
207
 
198
- public includesElement(elementId: M['Element']): boolean {
199
- return this.#includeElements.has(elementId)
208
+ /**
209
+ * Checks if the view has the given tag.
210
+ */
211
+ public isTagged(tag: aux.LooseTag<A>): boolean {
212
+ return this.tags.includes(tag as aux.Tag<A>)
213
+ }
214
+
215
+ public includesElement(element: aux.LooseElementId<A> | { id: aux.Fqn<A> }): boolean {
216
+ return this.#includeElements.has(getId(element))
200
217
  }
201
218
 
202
- public includesDeployment(deploymentId: M['Deployment']): boolean {
203
- return this.#includeDeployments.has(deploymentId)
219
+ public includesDeployment(deployment: aux.LooseDeploymentId<A> | { id: aux.DeploymentFqn<A> }): boolean {
220
+ return this.#includeDeployments.has(getId(deployment))
204
221
  }
205
222
 
206
- public includesRelation(relationId: M['RelationId']): boolean {
207
- return this.#includeRelations.has(relationId)
223
+ public includesRelation(relation: scalar.RelationId | { id: scalar.RelationId }): boolean {
224
+ return this.#includeRelations.has(getId(relation))
208
225
  }
209
226
 
210
227
  /**
211
228
  * Below are type guards.
212
229
  */
213
- public isComputed(): this is LikeC4ViewModel<M, ComputedView> {
214
- return true // Diagram view is a computed view
230
+ public isComputed(
231
+ this: LikeC4ViewModel<any, any>,
232
+ ): this is LikeC4ViewModel<aux.toComputed<A>> {
233
+ return this.$view[_stage] === 'computed'
234
+ }
235
+
236
+ public isDiagram(
237
+ this: LikeC4ViewModel<any, any>,
238
+ ): this is LikeC4ViewModel<aux.toLayouted<A>> {
239
+ return this.$view[_stage] === 'layouted'
240
+ }
241
+
242
+ public isElementView(this: LikeC4ViewModel<any, any>): this is LikeC4ViewModel.ElementView<A, V> {
243
+ return this.$view[_type] === 'element'
244
+ }
245
+
246
+ public isScopedElementView(
247
+ this: LikeC4ViewModel<any, any>,
248
+ ): this is LikeC4ViewModel.ScopedElementView<A, V> {
249
+ return this.$view[_type] === 'element' && isTruthy(this.$view.viewOf)
250
+ }
251
+
252
+ public isDeploymentView(this: LikeC4ViewModel<any, any>): this is LikeC4ViewModel.DeploymentView<A, V> {
253
+ return this.$view[_type] === 'deployment'
215
254
  }
216
255
 
217
- public isDiagram(): this is LikeC4ViewModel<M, DiagramView> {
218
- return 'bounds' in this.$view
256
+ public isDynamicView(this: LikeC4ViewModel<any, any>): this is LikeC4ViewModel.DynamicView<A, V> {
257
+ return this.$view[_type] === 'dynamic'
258
+ }
259
+ }
260
+
261
+ export namespace LikeC4ViewModel {
262
+ export interface ElementView<A extends Any, V extends $View<A> = $View<A>>
263
+ extends LikeC4ViewModel<A, ViewWithType<V, 'element'>>
264
+ {
219
265
  }
220
266
 
221
- public isElementView(): this is LikeC4ViewModel<M, ComputedElementView> {
222
- return isElementView(this.$view as LikeC4View)
267
+ export interface ScopedElementView<A extends Any, V extends $View<A> = $View<A>>
268
+ extends LikeC4ViewModel<A, ViewWithType<V, 'element'> & { viewOf: aux.StrictFqn<A> }>
269
+ {
270
+ viewOf: ElementModel<A>
223
271
  }
224
272
 
225
- public isDeploymentView(): this is LikeC4ViewModel<M, ComputedDeploymentView> {
226
- return isDeploymentView(this.$view as LikeC4View)
273
+ export interface DeploymentView<A extends Any, V extends $View<A> = $View<A>>
274
+ extends LikeC4ViewModel<A, ViewWithType<V, 'deployment'>>
275
+ {
227
276
  }
228
277
 
229
- public isDynamicView(): this is LikeC4ViewModel<M, ComputedDynamicView> {
230
- return isDynamicView(this.$view as LikeC4View)
278
+ export interface DynamicView<A extends Any, V extends $View<A> = $View<A>>
279
+ extends LikeC4ViewModel<A, ViewWithType<V, 'dynamic'>>
280
+ {
231
281
  }
232
282
  }
@@ -1,129 +1,110 @@
1
+ import { isTruthy } from 'remeda'
1
2
  import {
2
- type Color as C4Color,
3
- type ComputedView,
4
- type DeploymentNodeKind,
5
- type DiagramView,
3
+ type Any,
4
+ type Color,
5
+ type ComputedNode,
6
+ type DiagramNode,
6
7
  type ElementShape as C4ElementShape,
7
8
  type ElementStyle,
9
+ type ExtractOnStage,
8
10
  type IconUrl,
9
11
  type IteratorLike,
10
12
  type Link,
11
- type NodeId,
12
- type Tag as C4Tag,
13
- ComputedNode,
14
- ElementKind,
13
+ type scalar,
14
+ GroupElementKind,
15
+ isGroupElementKind,
15
16
  } from '../../types'
17
+ import type * as aux from '../../types/aux'
18
+ import { memoizeProp } from '../../utils'
16
19
  import type { DeployedInstanceModel, DeploymentElementModel } from '../DeploymentElementModel'
17
20
  import type { ElementModel } from '../ElementModel'
18
- import type { AnyAux, IncomingFilter, OutgoingFilter } from '../types'
21
+ import type { $View, IncomingFilter, OutgoingFilter } from '../types'
19
22
  import type { EdgesIterator } from './EdgeModel'
20
23
  import type { LikeC4ViewModel } from './LikeC4ViewModel'
21
24
 
22
- export type NodesIterator<M extends AnyAux, V extends ComputedView | DiagramView> = IteratorLike<NodeModel<M, V>>
25
+ export type NodesIterator<M extends Any, V extends $View<M>> = IteratorLike<NodeModel<M, V>>
23
26
 
24
- export namespace NodeModel {
25
- export interface WithParent<M extends AnyAux, V extends ComputedView | DiagramView> extends NodeModel<M, V> {
26
- parent: NodeModel<M, V>
27
- }
28
- export interface WithElement<M extends AnyAux, V extends ComputedView | DiagramView> extends NodeModel<M, V> {
29
- kind: ElementKind
30
- element: ElementModel<M>
31
- }
32
- export interface WithDeploymentElement<M extends AnyAux, V extends ComputedView | DiagramView>
33
- extends NodeModel<M, V>
34
- {
35
- kind: DeploymentNodeKind
36
- deployment: DeploymentElementModel<M>
37
- }
38
- export interface WithDeployedInstance<M extends AnyAux, V extends ComputedView | DiagramView>
39
- extends NodeModel<M, V>
40
- {
41
- kind: 'instance'
42
- element: ElementModel<M>
43
- deployment: DeployedInstanceModel<M>
44
- }
45
-
46
- export interface IsGroup<M extends AnyAux, V extends ComputedView | DiagramView> extends NodeModel<M, V> {
47
- kind: typeof ElementKind.Group
48
- element: null
49
- deployment: null
50
- }
51
- }
27
+ export class NodeModel<A extends Any = Any, V extends $View<A> = $View<A>> {
28
+ #node: ComputedNode<A> | DiagramNode<A>
52
29
 
53
- export class NodeModel<M extends AnyAux, V extends ComputedView | DiagramView = M['ViewType']> {
54
30
  constructor(
55
- public readonly $view: LikeC4ViewModel<M, V>,
31
+ public readonly $view: LikeC4ViewModel<A, V>,
56
32
  public readonly $node: V['nodes'][number],
57
33
  ) {
34
+ this.#node = $node
58
35
  }
59
36
 
60
- get id(): NodeId {
61
- return this.$node.id
37
+ get id(): scalar.NodeId {
38
+ return this.#node.id
62
39
  }
63
40
 
64
41
  get title(): string {
65
- return this.$node.title
42
+ return this.#node.title
66
43
  }
67
44
 
68
- get kind(): ElementKind | DeploymentNodeKind | typeof ElementKind.Group | 'instance' {
69
- return this.$node.kind as any
45
+ get kind(): aux.ElementKind<A> | aux.DeploymentKind<A> | typeof GroupElementKind | 'instance' {
46
+ return this.#node.kind as any
70
47
  }
71
48
 
72
49
  get description(): string | null {
73
- return this.$node.description
50
+ return this.#node.description ?? null
74
51
  }
75
52
 
76
53
  get technology(): string | null {
77
- return this.$node.technology
54
+ return this.#node.technology ?? null
78
55
  }
79
56
 
80
- get parent(): NodeModel<M, V> | null {
81
- return this.$node.parent ? this.$view.node(this.$node.parent) : null
57
+ get parent(): NodeModel<A, V> | null {
58
+ return this.#node.parent ? this.$view.node(this.#node.parent) : null
82
59
  }
83
60
 
84
- get element(): ElementModel<M> | null {
85
- const modelRef = ComputedNode.modelRef(this.$node)
61
+ get element(): ElementModel<A> | null {
62
+ const modelRef = this.#node.modelRef
86
63
  return modelRef ? this.$view.$model.element(modelRef) : null
87
64
  }
88
65
 
89
- get deployment(): DeploymentElementModel<M> | null {
90
- const modelRef = ComputedNode.deploymentRef(this.$node)
66
+ get deployment(): DeploymentElementModel<A> | null {
67
+ const modelRef = this.#node.deploymentRef
91
68
  return modelRef ? this.$view.$model.deployment.element(modelRef) : null
92
69
  }
93
70
 
94
71
  get shape(): C4ElementShape {
95
- return this.$node.shape
72
+ return this.#node.shape
96
73
  }
97
74
 
98
- get color(): C4Color {
99
- return this.$node.color
75
+ get color(): Color {
76
+ return this.#node.color
100
77
  }
101
78
 
102
79
  get icon(): IconUrl | null {
103
- return this.$node.icon ?? null
80
+ return this.#node.icon ?? null
104
81
  }
105
82
 
106
- get tags(): ReadonlyArray<C4Tag> {
107
- return this.$node.tags ?? []
83
+ get tags(): aux.Tags<A> {
84
+ return this.#node.tags
108
85
  }
109
86
 
110
87
  get links(): ReadonlyArray<Link> {
111
- return this.$node.links ?? []
88
+ return this.#node.links ?? []
112
89
  }
113
90
 
114
- get navigateTo(): LikeC4ViewModel<M> | null {
115
- return this.$node.navigateTo ? this.$view.$model.view(this.$node.navigateTo) : null
91
+ get navigateTo(): LikeC4ViewModel<A> | null {
92
+ return this.#node.navigateTo ? this.$view.$model.view(this.#node.navigateTo) : null
116
93
  }
117
94
 
118
95
  get style(): ElementStyle {
119
- return this.$node.style
96
+ return this.#node.style
97
+ }
98
+
99
+ public children(): ReadonlySet<NodeModel<A, V>> {
100
+ return memoizeProp(this, 'children', () => new Set(this.#node.children.map((child) => this.$view.node(child))))
120
101
  }
121
102
 
122
103
  /**
123
104
  * Get all ancestor elements (i.e. parent, parent’s parent, etc.)
124
105
  * (from closest to root)
125
106
  */
126
- public *ancestors(): NodesIterator<M, V> {
107
+ public *ancestors(): NodesIterator<A, V> {
127
108
  let parent = this.parent
128
109
  while (parent) {
129
110
  yield parent
@@ -132,25 +113,18 @@ export class NodeModel<M extends AnyAux, V extends ComputedView | DiagramView =
132
113
  return
133
114
  }
134
115
 
135
- public *children(): NodesIterator<M, V> {
136
- for (const child of this.$node.children) {
137
- yield this.$view.node(child)
138
- }
139
- return
140
- }
141
-
142
- public *sublings(): NodesIterator<M, V> {
143
- const sublings = this.parent?.children() ?? this.$view.roots()
144
- for (const subling of sublings) {
145
- if (subling.id !== this.id) {
146
- yield subling
116
+ public *siblings(): NodesIterator<A, V> {
117
+ const siblings = this.parent?.children() ?? this.$view.roots()
118
+ for (const sibling of siblings) {
119
+ if (sibling.id !== this.id) {
120
+ yield sibling
147
121
  }
148
122
  }
149
123
  return
150
124
  }
151
125
 
152
- public *incoming(filter: IncomingFilter = 'all'): EdgesIterator<M, V> {
153
- for (const edgeId of this.$node.inEdges) {
126
+ public *incoming(filter: IncomingFilter = 'all'): EdgesIterator<A, V> {
127
+ for (const edgeId of this.#node.inEdges) {
154
128
  const edge = this.$view.edge(edgeId)
155
129
  switch (true) {
156
130
  case filter === 'all':
@@ -163,8 +137,8 @@ export class NodeModel<M extends AnyAux, V extends ComputedView | DiagramView =
163
137
  return
164
138
  }
165
139
 
166
- public *incomers(filter: IncomingFilter = 'all'): NodesIterator<M, V> {
167
- const unique = new Set<NodeId>()
140
+ public *incomers(filter: IncomingFilter = 'all'): NodesIterator<A, V> {
141
+ const unique = new Set<aux.NodeId>()
168
142
  for (const r of this.incoming(filter)) {
169
143
  if (unique.has(r.source.id)) {
170
144
  continue
@@ -175,8 +149,8 @@ export class NodeModel<M extends AnyAux, V extends ComputedView | DiagramView =
175
149
  return
176
150
  }
177
151
 
178
- public *outgoing(filter: OutgoingFilter = 'all'): EdgesIterator<M, V> {
179
- for (const edgeId of this.$node.outEdges) {
152
+ public *outgoing(filter: OutgoingFilter = 'all'): EdgesIterator<A, V> {
153
+ for (const edgeId of this.#node.outEdges) {
180
154
  const edge = this.$view.edge(edgeId)
181
155
  switch (true) {
182
156
  case filter === 'all':
@@ -189,8 +163,8 @@ export class NodeModel<M extends AnyAux, V extends ComputedView | DiagramView =
189
163
  return
190
164
  }
191
165
 
192
- public *outgoers(filter: OutgoingFilter = 'all'): NodesIterator<M, V> {
193
- const unique = new Set<NodeId>()
166
+ public *outgoers(filter: OutgoingFilter = 'all'): NodesIterator<A, V> {
167
+ const unique = new Set<aux.NodeId>()
194
168
  for (const r of this.outgoing(filter)) {
195
169
  if (unique.has(r.target.id)) {
196
170
  continue
@@ -201,39 +175,71 @@ export class NodeModel<M extends AnyAux, V extends ComputedView | DiagramView =
201
175
  return
202
176
  }
203
177
 
204
- public isDiagramNode(): this is NodeModel<M, DiagramView> {
205
- return 'width' in this.$node && 'height' in this.$node
178
+ public isDiagramNode(this: NodeModel<any, any>): this is NodeModel<A, ExtractOnStage<V, 'layouted'>> {
179
+ return 'width' in this.#node && 'height' in this.#node
206
180
  }
207
181
 
208
182
  public hasChildren(): boolean {
209
- return this.$node.children.length > 0
183
+ return this.#node.children.length > 0
210
184
  }
211
185
 
212
- public hasParent(): this is NodeModel.WithParent<M, V> {
213
- return this.$node.parent !== null
186
+ public hasParent(): this is NodeModel.WithParent<A, V> {
187
+ return this.#node.parent !== null
214
188
  }
215
189
 
216
190
  /**
217
191
  * Check if this node references to logical model element.
218
192
  */
219
- public hasElement(): this is NodeModel.WithElement<M, V> {
220
- return ComputedNode.modelRef(this.$node) !== null
193
+ public hasElement(): this is NodeModel.WithElement<A, V> {
194
+ return isTruthy(this.#node.modelRef)
221
195
  }
222
196
  /**
223
197
  * Check if this node references to deployment element (Node or Instance).
224
198
  */
225
- public hasDeployment(): this is NodeModel.WithDeploymentElement<M, V> {
226
- return ComputedNode.deploymentRef(this.$node) !== null
199
+ public hasDeployment(): this is NodeModel.WithDeploymentElement<A, V> {
200
+ return isTruthy(this.#node.deploymentRef)
227
201
  }
228
202
  /**
229
203
  * Check if this node references to deployed instance
230
204
  * Deployed instance always references to element and deployment element.
231
205
  */
232
- public hasDeployedInstance(): this is NodeModel.WithDeployedInstance<M, V> {
206
+ public hasDeployedInstance(): this is NodeModel.WithDeployedInstance<A, V> {
233
207
  return this.hasElement() && this.hasDeployment()
234
208
  }
235
209
 
236
- public isGroup(): this is NodeModel.IsGroup<M, V> {
237
- return ComputedNode.isNodesGroup(this.$node)
210
+ public isGroup(): this is NodeModel.IsGroup<A, V> {
211
+ return isGroupElementKind(this.#node)
212
+ }
213
+
214
+ /**
215
+ * Checks if the node has the given tag.
216
+ */
217
+ public isTagged(tag: aux.LooseTag<A>): boolean {
218
+ return this.tags.includes(tag as aux.Tag<A>)
219
+ }
220
+ }
221
+
222
+ export namespace NodeModel {
223
+ export interface WithParent<A extends Any = Any, V extends $View<A> = $View<A>> extends NodeModel<A, V> {
224
+ parent: NodeModel<A, V>
225
+ }
226
+ export interface WithElement<A extends Any = Any, V extends $View<A> = $View<A>> extends NodeModel<A, V> {
227
+ kind: aux.ElementKind<A>
228
+ element: ElementModel<A>
229
+ }
230
+ export interface WithDeploymentElement<A extends Any = Any, V extends $View<A> = $View<A>> extends NodeModel<A, V> {
231
+ kind: aux.DeploymentKind<A>
232
+ deployment: DeploymentElementModel<A>
233
+ }
234
+ export interface WithDeployedInstance<A extends Any = Any, V extends $View<A> = $View<A>> extends NodeModel<A, V> {
235
+ kind: 'instance'
236
+ element: ElementModel<A>
237
+ deployment: DeployedInstanceModel<A>
238
+ }
239
+
240
+ export interface IsGroup<A extends Any = Any, V extends $View<A> = $View<A>> extends NodeModel<A, V> {
241
+ kind: typeof GroupElementKind
242
+ element: null
243
+ deployment: null
238
244
  }
239
245
  }
@@ -1,4 +1,4 @@
1
- import type { ElementThemeColors, ElementThemeColorValues } from '../types/theme'
1
+ import type { ElementThemeColors, ElementThemeColorValues } from '../types'
2
2
 
3
3
  const blue = {
4
4
  fill: '#3b82f6',