@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,226 @@
1
+ import { sort } from 'remeda'
2
+ import { nonNullable } from '../../errors'
3
+ import type { IteratorLike } from '../../types'
4
+ import type { Link, Tag } from '../../types/element'
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 { compareByFqnHierarchically, getOrCreate } from '../../utils'
20
+ import type { ElementModel } from '../ElementModel'
21
+ import type { LikeC4Model } from '../LikeC4Model'
22
+ import { type AnyAux, getId } from '../types'
23
+ import { type EdgesIterator, EdgeModel } from './EdgeModel'
24
+ import { type NodesIterator, NodeModel } from './NodeModel'
25
+
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 Map<Tag, 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 sort($view.nodes, compareByFqnHierarchically)) {
42
+ const el = new NodeModel(this, Object.freeze(node))
43
+ this.#nodes.set(node.id, el)
44
+ if (!node.parent) {
45
+ this.#rootnodes.add(el)
46
+ }
47
+ if (el.hasDeployment()) {
48
+ this.#includeDeployments.add(el.deployment.id)
49
+ }
50
+ if (el.hasElement()) {
51
+ this.#includeElements.add(el.element.id)
52
+ }
53
+ for (const tag of el.tags) {
54
+ getOrCreate(this.#allTags, tag, () => new Set()).add(el)
55
+ }
56
+ }
57
+
58
+ for (const edge of $view.edges) {
59
+ const edgeModel = new EdgeModel(
60
+ this,
61
+ Object.freeze(edge),
62
+ this.node(edge.source),
63
+ this.node(edge.target),
64
+ )
65
+ for (const tag of edgeModel.tags) {
66
+ getOrCreate(this.#allTags, tag, () => new Set()).add(edgeModel)
67
+ }
68
+ for (const rel of edge.relations) {
69
+ this.#includeRelations.add(rel)
70
+ }
71
+ this.#edges.set(edge.id, edgeModel)
72
+ }
73
+ }
74
+
75
+ get __(): NonNullable<M['ViewType']['__']> {
76
+ return this.$view.__ ?? 'element'
77
+ }
78
+
79
+ get id(): M['ViewId'] {
80
+ return this.$view.id
81
+ }
82
+
83
+ get title(): string | null {
84
+ return this.$view.title
85
+ }
86
+
87
+ get tags(): ReadonlyArray<Tag> {
88
+ return this.$view.tags ?? []
89
+ }
90
+
91
+ get links(): ReadonlyArray<Link> {
92
+ return this.$view.links ?? []
93
+ }
94
+
95
+ get viewOf(): ElementModel<M> | null {
96
+ const v = this.$view as LikeC4View
97
+ if (isScopedElementView(v)) {
98
+ return this.$model.element(v.viewOf)
99
+ }
100
+ return null
101
+ }
102
+
103
+ /**
104
+ * All tags from nodes and edges.
105
+ */
106
+ get includedTags(): ReadonlyArray<Tag> {
107
+ return [...this.#allTags.keys()]
108
+ }
109
+
110
+ public roots(): NodesIterator<M, V> {
111
+ return this.#rootnodes.values()
112
+ }
113
+
114
+ /**
115
+ * Iterate over all nodes that have children.
116
+ */
117
+ public *compounds(): NodesIterator<M, V> {
118
+ for (const node of this.#nodes.values()) {
119
+ if (node.hasChildren()) {
120
+ yield node
121
+ }
122
+ }
123
+ return
124
+ }
125
+
126
+ /**
127
+ * Get node by id.
128
+ * @throws Error if node is not found.
129
+ */
130
+ public node(node: M['NodeOrId']): NodeModel<M, V> {
131
+ const nodeId = getId(node)
132
+ return nonNullable(this.#nodes.get(nodeId), `Node ${nodeId} not found in view ${this.$view.id}`)
133
+ }
134
+
135
+ /**
136
+ * Find node by id.
137
+ */
138
+ public findNode(node: M['NodeOrId']): NodeModel<M, V> | null {
139
+ return this.#nodes.get(getId(node) as C4NodeId) ?? null
140
+ }
141
+ /**
142
+ * Iterate over all nodes.
143
+ */
144
+ public nodes(): NodesIterator<M, V> {
145
+ return this.#nodes.values()
146
+ }
147
+
148
+ /**
149
+ * Find edge by id.
150
+ * @param edge Edge or id
151
+ * @returns EdgeModel
152
+ */
153
+ public edge(edge: M['EdgeOrId']): EdgeModel<M, V> {
154
+ const edgeId = getId(edge) as C4EdgeId
155
+ return nonNullable(this.#edges.get(edgeId), `Edge ${edgeId} not found in view ${this.$view.id}`)
156
+ }
157
+ public findEdge(edge: M['EdgeOrId']): EdgeModel<M, V> | null {
158
+ return this.#edges.get(getId(edge) as C4EdgeId) ?? null
159
+ }
160
+ /**
161
+ * Iterate over all edges.
162
+ */
163
+ public edges(): EdgesIterator<M, V> {
164
+ return this.#edges.values()
165
+ }
166
+
167
+ /**
168
+ * Iterate over all edges.
169
+ */
170
+ public *edgesWithRelation(relation: M['RelationId']): EdgesIterator<M, V> {
171
+ for (const edge of this.#edges.values()) {
172
+ if (edge.includesRelation(relation)) {
173
+ yield edge
174
+ }
175
+ }
176
+ return
177
+ }
178
+
179
+ /**
180
+ * Nodes that have references to elements from logical model.
181
+ */
182
+ public *elements(): IteratorLike<NodeModel.WithElement<M, V>> {
183
+ // return this.#nodes.values().filter(node => node.hasElement())
184
+ for (const node of this.#nodes.values()) {
185
+ if (node.hasElement()) {
186
+ yield node
187
+ }
188
+ }
189
+ return
190
+ }
191
+
192
+ public includesElement(elementId: M['Element']): boolean {
193
+ return this.#includeElements.has(elementId)
194
+ }
195
+
196
+ public includesDeployment(deploymentId: M['Deployment']): boolean {
197
+ return this.#includeDeployments.has(deploymentId)
198
+ }
199
+
200
+ public includesRelation(relationId: M['RelationId']): boolean {
201
+ return this.#includeRelations.has(relationId)
202
+ }
203
+
204
+ /**
205
+ * Below are type guards.
206
+ */
207
+ public isComputed(): this is LikeC4ViewModel<M, ComputedView> {
208
+ return true // Diagram view is a computed view
209
+ }
210
+
211
+ public isDiagram(): this is LikeC4ViewModel<M, DiagramView> {
212
+ return 'bounds' in this.$view
213
+ }
214
+
215
+ public isElementView(): this is LikeC4ViewModel<M, ComputedElementView> {
216
+ return isElementView(this.$view as LikeC4View)
217
+ }
218
+
219
+ public isDeploymentView(): this is LikeC4ViewModel<M, ComputedDeploymentView> {
220
+ return isDeploymentView(this.$view as LikeC4View)
221
+ }
222
+
223
+ public isDynamicView(): this is LikeC4ViewModel<M, ComputedDynamicView> {
224
+ return isDynamicView(this.$view as LikeC4View)
225
+ }
226
+ }
@@ -0,0 +1,229 @@
1
+ import {
2
+ type Color as C4Color,
3
+ type ComputedView,
4
+ type DeploymentNodeKind,
5
+ type DiagramView,
6
+ type ElementShape as C4ElementShape,
7
+ type IteratorLike,
8
+ type Link,
9
+ type NodeId,
10
+ type Tag as C4Tag,
11
+ ComputedNode,
12
+ ElementKind,
13
+ } from '../../types'
14
+ import type { DeployedInstanceModel, DeploymentElementModel } from '../DeploymentElementModel'
15
+ import type { ElementModel } from '../ElementModel'
16
+ import type { AnyAux, IncomingFilter, OutgoingFilter } from '../types'
17
+ import type { EdgesIterator } from './EdgeModel'
18
+ import type { LikeC4ViewModel } from './LikeC4ViewModel'
19
+
20
+ export type NodesIterator<M extends AnyAux, V extends ComputedView | DiagramView> = IteratorLike<NodeModel<M, V>>
21
+
22
+ export namespace NodeModel {
23
+ export interface WithParent<M extends AnyAux, V extends ComputedView | DiagramView> extends NodeModel<M, V> {
24
+ parent: NodeModel<M, V>
25
+ }
26
+ export interface WithElement<M extends AnyAux, V extends ComputedView | DiagramView> extends NodeModel<M, V> {
27
+ kind: ElementKind
28
+ element: ElementModel<M>
29
+ }
30
+ export interface WithDeploymentElement<M extends AnyAux, V extends ComputedView | DiagramView>
31
+ extends NodeModel<M, V>
32
+ {
33
+ kind: DeploymentNodeKind
34
+ deployment: DeploymentElementModel<M>
35
+ }
36
+ export interface WithDeployedInstance<M extends AnyAux, V extends ComputedView | DiagramView>
37
+ extends NodeModel<M, V>
38
+ {
39
+ kind: 'instance'
40
+ element: ElementModel<M>
41
+ deployment: DeployedInstanceModel<M>
42
+ }
43
+
44
+ export interface IsGroup<M extends AnyAux, V extends ComputedView | DiagramView> extends NodeModel<M, V> {
45
+ kind: typeof ElementKind.Group
46
+ element: null
47
+ deployment: null
48
+ }
49
+ }
50
+
51
+ export class NodeModel<M extends AnyAux, V extends ComputedView | DiagramView = M['ViewType']> {
52
+ constructor(
53
+ public readonly $view: LikeC4ViewModel<M, V>,
54
+ public readonly $node: V['nodes'][number],
55
+ ) {
56
+ }
57
+
58
+ get id(): NodeId {
59
+ return this.$node.id
60
+ }
61
+
62
+ get title(): string {
63
+ return this.$node.title
64
+ }
65
+
66
+ get kind(): ElementKind | DeploymentNodeKind | typeof ElementKind.Group | 'instance' {
67
+ return this.$node.kind as any
68
+ }
69
+
70
+ get description(): string | null {
71
+ return this.$node.description
72
+ }
73
+
74
+ get technology(): string | null {
75
+ return this.$node.technology
76
+ }
77
+
78
+ get parent(): NodeModel<M, V> | null {
79
+ return this.$node.parent ? this.$view.node(this.$node.parent) : null
80
+ }
81
+
82
+ get element(): ElementModel<M> | null {
83
+ const modelRef = ComputedNode.modelRef(this.$node)
84
+ return modelRef ? this.$view.$model.element(modelRef) : null
85
+ }
86
+
87
+ get deployment(): DeploymentElementModel<M> | null {
88
+ const modelRef = ComputedNode.deploymentRef(this.$node)
89
+ return modelRef ? this.$view.$model.deployment.element(modelRef) : null
90
+ }
91
+
92
+ get shape(): C4ElementShape {
93
+ return this.$node.shape
94
+ }
95
+
96
+ get color(): C4Color {
97
+ return this.$node.color
98
+ }
99
+
100
+ get tags(): ReadonlyArray<C4Tag> {
101
+ return this.$node.tags ?? []
102
+ }
103
+
104
+ get links(): ReadonlyArray<Link> {
105
+ return this.$node.links ?? []
106
+ }
107
+
108
+ get navigateTo(): LikeC4ViewModel<M> | null {
109
+ return this.$node.navigateTo ? this.$view.$model.view(this.$node.navigateTo) : null
110
+ }
111
+
112
+ /**
113
+ * Get all ancestor elements (i.e. parent, parent’s parent, etc.)
114
+ * (from closest to root)
115
+ */
116
+ public *ancestors(): NodesIterator<M, V> {
117
+ let parent = this.parent
118
+ while (parent) {
119
+ yield parent
120
+ parent = parent.parent
121
+ }
122
+ return
123
+ }
124
+
125
+ public *children(): NodesIterator<M, V> {
126
+ for (const child of this.$node.children) {
127
+ yield this.$view.node(child)
128
+ }
129
+ return
130
+ }
131
+
132
+ public *sublings(): NodesIterator<M, V> {
133
+ const sublings = this.parent?.children() ?? this.$view.roots()
134
+ for (const subling of sublings) {
135
+ if (subling.id !== this.id) {
136
+ yield subling
137
+ }
138
+ }
139
+ return
140
+ }
141
+
142
+ public *incoming(filter: IncomingFilter = 'all'): EdgesIterator<M, V> {
143
+ for (const edgeId of this.$node.inEdges) {
144
+ const edge = this.$view.edge(edgeId)
145
+ switch (true) {
146
+ case filter === 'all':
147
+ case filter === 'direct' && edge.target.id === this.id:
148
+ case filter === 'to-descendants' && edge.target.id !== this.id:
149
+ yield edge
150
+ break
151
+ }
152
+ }
153
+ return
154
+ }
155
+
156
+ public *incomers(filter: IncomingFilter = 'all'): NodesIterator<M, V> {
157
+ const unique = new Set<NodeId>()
158
+ for (const r of this.incoming(filter)) {
159
+ if (unique.has(r.source.id)) {
160
+ continue
161
+ }
162
+ unique.add(r.source.id)
163
+ yield r.source
164
+ }
165
+ return
166
+ }
167
+
168
+ public *outgoing(filter: OutgoingFilter = 'all'): EdgesIterator<M, V> {
169
+ for (const edgeId of this.$node.outEdges) {
170
+ const edge = this.$view.edge(edgeId)
171
+ switch (true) {
172
+ case filter === 'all':
173
+ case filter === 'direct' && edge.source.id === this.id:
174
+ case filter === 'from-descendants' && edge.source.id !== this.id:
175
+ yield edge
176
+ break
177
+ }
178
+ }
179
+ return
180
+ }
181
+
182
+ public *outgoers(filter: OutgoingFilter = 'all'): NodesIterator<M, V> {
183
+ const unique = new Set<NodeId>()
184
+ for (const r of this.outgoing(filter)) {
185
+ if (unique.has(r.target.id)) {
186
+ continue
187
+ }
188
+ unique.add(r.target.id)
189
+ yield r.target
190
+ }
191
+ return
192
+ }
193
+
194
+ public isDiagramNode(): this is NodeModel<M, DiagramView> {
195
+ return 'width' in this.$node && 'height' in this.$node
196
+ }
197
+
198
+ public hasChildren(): boolean {
199
+ return this.$node.children.length > 0
200
+ }
201
+
202
+ public hasParent(): this is NodeModel.WithParent<M, V> {
203
+ return this.$node.parent !== null
204
+ }
205
+
206
+ /**
207
+ * Check if this node references to logical model element.
208
+ */
209
+ public hasElement(): this is NodeModel.WithElement<M, V> {
210
+ return ComputedNode.modelRef(this.$node) !== null
211
+ }
212
+ /**
213
+ * Check if this node references to deployment element (Node or Instance).
214
+ */
215
+ public hasDeployment(): this is NodeModel.WithDeploymentElement<M, V> {
216
+ return ComputedNode.deploymentRef(this.$node) !== null
217
+ }
218
+ /**
219
+ * Check if this node references to deployed instance
220
+ * Deployed instance always references to element and deployment element.
221
+ */
222
+ public hasDeployedInstance(): this is NodeModel.WithDeployedInstance<M, V> {
223
+ return this.hasElement() && this.hasDeployment()
224
+ }
225
+
226
+ public isGroup(): this is NodeModel.IsGroup<M, V> {
227
+ return ComputedNode.isNodesGroup(this.$node)
228
+ }
229
+ }
@@ -1,4 +1,4 @@
1
- import type { Tagged } from 'type-fest'
1
+ import type { Simplify, Tagged, UnionToIntersection } from 'type-fest'
2
2
 
3
3
  export type NonEmptyArray<T> = [T, ...T[]]
4
4
 
@@ -14,3 +14,57 @@ export interface XYPoint {
14
14
  x: number
15
15
  y: number
16
16
  }
17
+
18
+ export type KeysOf<T> = keyof T extends infer K extends string ? K : never
19
+
20
+ type AllNever<Expressions> = UnionToIntersection<
21
+ {
22
+ [Name in keyof Expressions]: {
23
+ -readonly [Key in keyof Expressions[Name]]?: never
24
+ }
25
+ }[keyof Expressions]
26
+ >
27
+
28
+ /**
29
+ * @example
30
+ * type Variant1 = {
31
+ * a: string
32
+ * }
33
+ * type Variant2 = {
34
+ * b: number
35
+ * }
36
+ *
37
+ * type Variants = ExclusiveUnion<{
38
+ * Variant1: Variant1,
39
+ * Variant2: Variant2
40
+ * }>
41
+ *
42
+ * Fail here
43
+ * const variant1: Variants = {
44
+ * a: 'one',
45
+ * b: 1
46
+ * }
47
+ */
48
+
49
+ export type ExclusiveUnion<Expressions> = Expressions extends object ? {
50
+ [Name in keyof Expressions]: Simplify<Omit<AllNever<Expressions>, keyof Expressions[Name]> & Expressions[Name]>
51
+ }[keyof Expressions]
52
+ : Expressions
53
+
54
+ /**
55
+ * Copy from https://github.com/remeda/remeda/blob/main/src/internal/types/NTuple.ts
56
+ * An array with *exactly* N elements in it.
57
+ *
58
+ * Only literal N values are supported. For very large N the type might result
59
+ * in a recurse depth error. For negative N the type would result in an infinite
60
+ * recursion. None of these have protections because this is an internal type!
61
+ */
62
+ export type NTuple<
63
+ T,
64
+ N extends number,
65
+ Result extends Array<unknown> = [],
66
+ > = Result['length'] extends N ? Result : NTuple<T, N, [...Result, T]>
67
+
68
+ export type IteratorLike<T> = IteratorObject<T, BuiltinIteratorReturn>
69
+
70
+ export type Predicate<T> = (x: T) => boolean
@@ -0,0 +1,169 @@
1
+ import type { MergeExclusive, Simplify, Tagged } from 'type-fest'
2
+ import type { IconUrl, NonEmptyArray } from './_common'
3
+ import type { ElementShape, ElementStyle, Fqn, Link, Tag } from './element'
4
+ import type { AbstractRelation, RelationId } from './relation'
5
+ import type { Color } from './theme'
6
+
7
+ export type DeploymentNodeKind<Kinds extends string = string> = Tagged<Kinds, 'DeploymentNodeKind'>
8
+
9
+ export type DeploymentElementStyle = ElementStyle & {
10
+ readonly icon?: IconUrl
11
+ readonly shape?: ElementShape
12
+ readonly color?: Color
13
+ }
14
+
15
+ export interface DeploymentNodeKindSpecification {
16
+ readonly technology?: string
17
+ readonly notation?: string
18
+ readonly style: DeploymentElementStyle
19
+ }
20
+
21
+ export interface DeploymentNode {
22
+ // Full-qualified-name for Deployment model
23
+ readonly id: Fqn
24
+ readonly kind: DeploymentNodeKind
25
+ readonly title: string
26
+ readonly description?: string | null
27
+ readonly technology?: string | null
28
+ readonly tags?: NonEmptyArray<Tag> | null
29
+ readonly links?: NonEmptyArray<Link> | null
30
+ readonly style?: DeploymentElementStyle
31
+ readonly notation?: string
32
+ readonly metadata?: Record<string, string>
33
+ }
34
+
35
+ export interface DeployedInstance {
36
+ /**
37
+ * Format: `<DeploymentNode Fqn>.<Instance Id>`
38
+ * i.e parent fqn is deployment target
39
+ */
40
+ readonly id: Fqn
41
+ readonly element: Fqn
42
+ readonly title?: string
43
+ readonly description?: string | null
44
+ readonly technology?: string | null
45
+ readonly tags?: NonEmptyArray<Tag> | null
46
+ readonly links?: NonEmptyArray<Link> | null
47
+ readonly style?: DeploymentElementStyle
48
+ readonly notation?: string
49
+ readonly metadata?: Record<string, string>
50
+ }
51
+
52
+ export type DeploymentElement = Simplify<MergeExclusive<DeploymentNode, DeployedInstance>>
53
+
54
+ export namespace DeploymentElement {
55
+ export const isDeploymentNode = (el: DeploymentElement): el is DeploymentNode => {
56
+ return 'kind' in el && !('element' in el)
57
+ }
58
+ export const isInstance = (el: DeploymentElement): el is DeployedInstance => {
59
+ return 'element' in el && !('kind' in el)
60
+ }
61
+ }
62
+
63
+ export type PredicateSelector =
64
+ | 'children' // ele.*
65
+ | 'expanded' // ele._
66
+ | 'descendants' // ele.**
67
+
68
+ export interface DeploymentRef {
69
+ // Reference to DeploymentNode or DeployedInstance
70
+ readonly id: Fqn
71
+ // Reference to element within DeployedInstance
72
+ readonly element?: Fqn
73
+ }
74
+
75
+ /**
76
+ * NOTE:
77
+ */
78
+ export interface DeploymentRelation extends AbstractRelation {
79
+ readonly id: RelationId
80
+ readonly source: DeploymentRef
81
+ readonly target: DeploymentRef
82
+ }
83
+
84
+ // export namespace DeploymentRelationExpression {
85
+ // // type LogicalRefEndpoint = {
86
+ // // logicalRef: ElementRefExpr
87
+ // // }
88
+ // // export type DirectEndpoint = ExclusiveUnion<{
89
+ // // DeploymentElementExpression: DeploymentElementExpression
90
+ // // Logical: LogicalRefEndpoint
91
+ // // }>
92
+
93
+ // // export const isLogicalEndpoint = (expr: DirectEndpoint): expr is LogicalRefEndpoint => {
94
+ // // return 'logicalRef' in expr
95
+ // // }
96
+
97
+ // export type Direct = {
98
+ // source: FqnExpression.Element
99
+ // target: FqnExpression.Element
100
+ // isBidirectional?: boolean
101
+ // }
102
+ // export const isDirect = (expr: DeploymentExpression): expr is Direct => {
103
+ // return 'source' in expr && 'target' in expr
104
+ // }
105
+ // export type Incoming = {
106
+ // incoming: FqnExpression.Element
107
+ // }
108
+ // export const isIncoming = (expr: DeploymentExpression): expr is Incoming => {
109
+ // return 'incoming' in expr
110
+ // }
111
+ // export type Outgoing = {
112
+ // outgoing: FqnExpression.Element
113
+ // }
114
+ // export const isOutgoing = (expr: DeploymentExpression): expr is Outgoing => {
115
+ // return 'outgoing' in expr
116
+ // }
117
+ // export type InOut = {
118
+ // inout: FqnExpression.Element
119
+ // }
120
+ // export const isInOut = (expr: DeploymentExpression): expr is InOut => {
121
+ // return 'inout' in expr
122
+ // }
123
+ // }
124
+
125
+ // export type DeploymentRelationExpression = ExclusiveUnion<{
126
+ // Direct: DeploymentRelationExpression.Direct
127
+ // Incoming: DeploymentRelationExpression.Incoming
128
+ // Outgoing: DeploymentRelationExpression.Outgoing
129
+ // InOut: DeploymentRelationExpression.InOut
130
+ // }>
131
+
132
+ // export namespace DeploymentElementExpression {
133
+ // export type Ref = {
134
+ // ref: DeploymentRef
135
+ // selector?: PredicateSelector
136
+ // }
137
+ // export const isRef = (expr: DeploymentExpression): expr is Ref => {
138
+ // return 'ref' in expr
139
+ // }
140
+
141
+ // export type Wildcard = {
142
+ // wildcard: true
143
+ // }
144
+ // export const isWildcard = (expr: DeploymentExpression): expr is Wildcard => {
145
+ // return 'wildcard' in expr && expr.wildcard === true
146
+ // }
147
+ // }
148
+
149
+ // export type DeploymentElementExpression = ExclusiveUnion<{
150
+ // DeploymentRef: DeploymentElementExpression.Ref
151
+ // Wildcard: DeploymentElementExpression.Wildcard
152
+ // }>
153
+
154
+ // export type DeploymentExpression = ExclusiveUnion<{
155
+ // Element: DeploymentElementExpression
156
+ // Relation: DeploymentRelationExpression
157
+ // }>
158
+
159
+ // export namespace DeploymentExpression {
160
+ // export type Element = DeploymentElementExpression
161
+ // export type Relation = DeploymentRelationExpression
162
+
163
+ // export const isElement = (expr: DeploymentExpression): expr is DeploymentElementExpression => {
164
+ // return DeploymentElementExpression.isRef(expr) || DeploymentElementExpression.isWildcard(expr)
165
+ // }
166
+ // export const isRelation = (expr: DeploymentExpression): expr is DeploymentRelationExpression => {
167
+ // return !DeploymentExpression.isElement(expr)
168
+ // }
169
+ // }
@@ -28,13 +28,17 @@ export const ElementShapes = [
28
28
  ] as const
29
29
 
30
30
  export type ElementShape = TupleToUnion<typeof ElementShapes>
31
- export const DefaultThemeColor = 'primary' satisfies ThemeColor
32
- export const DefaultElementShape = 'rectangle' satisfies ElementShape
31
+ export const DefaultThemeColor: ThemeColor = 'primary'
32
+ export const DefaultElementShape: ElementShape = 'rectangle'
33
33
 
34
34
  export interface ElementStyle {
35
- border?: BorderStyle
36
- // 0-100
37
- opacity?: number
35
+ readonly border?: BorderStyle
36
+ /**
37
+ * In percentage 0-100, 0 is fully transparent
38
+ *
39
+ * @default 100
40
+ */
41
+ readonly opacity?: number
38
42
  }
39
43
 
40
44
  export type Tag<Tags extends string = string> = Tagged<Tags, 'Tag'>