@likec4/core 1.31.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 +9 -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.Bq753SYD.mjs → core.CBpKAlhj.mjs} +2012 -4422
  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 +15 -7
  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 +171 -144
  109. package/src/model/DeploymentModel.ts +88 -65
  110. package/src/model/ElementModel.ts +85 -74
  111. package/src/model/LikeC4Model.ts +430 -241
  112. package/src/model/RelationModel.ts +63 -29
  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.CQDU11hR.d.mts +0 -753
  169. package/dist/shared/core.DJvmfnEi.d.mts +0 -1153
  170. package/dist/shared/core.DgfwjBtu.mjs +0 -495
  171. package/dist/shared/core.Dx7siloV.d.mts +0 -146
  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,18 +1,21 @@
1
1
  import type { Simplify } from 'type-fest'
2
- import { nonNullable } from '../../errors'
2
+ import type { ElementModel } from '../../model'
3
3
  import {
4
+ type AnyAux,
5
+ type aux,
4
6
  type ComputedNode,
5
7
  type Element,
6
- type Fqn,
7
- type NodeId,
8
+ type scalar,
9
+ type Unknown,
8
10
  DefaultElementShape,
9
11
  DefaultThemeColor,
10
- ElementKind,
12
+ GroupElementKind,
11
13
  } from '../../types'
14
+ import { nonNullable } from '../../utils'
12
15
  import { compareByFqnHierarchically, parentFqn } from '../../utils/fqn'
13
16
  import { NodesGroup } from '../element-view/memory'
14
17
 
15
- function updateDepthOfAncestors(node: ComputedNode, nodes: ReadonlyMap<Fqn, ComputedNode>) {
18
+ function updateDepthOfAncestors(node: ComputedNode, nodes: ReadonlyMap<string, ComputedNode>) {
16
19
  let parentNd
17
20
  while (!!node.parent && (parentNd = nodes.get(node.parent))) {
18
21
  const depth = parentNd.depth ?? 1
@@ -25,30 +28,27 @@ function updateDepthOfAncestors(node: ComputedNode, nodes: ReadonlyMap<Fqn, Comp
25
28
  }
26
29
  }
27
30
 
28
- const modelElementAsNodeSource = (element: Element): ComputedNodeSource => {
29
- return {
30
- ...element,
31
- modelRef: 1,
32
- }
33
- }
34
-
35
- // type ComputedNodeSource = Simplify<SetRequired<Partial<Omit<ComputedNode, 'parent' | 'children' | 'inEdges' | 'outEdges' | 'level' | 'depth'>>, 'id' | 'title' | 'kind'>>
36
- export type ComputedNodeSource = Simplify<
37
- & Pick<ComputedNode, 'id' | 'title' | 'kind' | 'deploymentRef' | 'modelRef'>
38
- & Partial<Omit<Element, 'id' | 'title' | 'kind'>>
31
+ export type ComputedNodeSource<A extends AnyAux = Unknown> = Simplify<
32
+ & Pick<ComputedNode<A>, 'id' | 'title' | 'kind' | 'deploymentRef' | 'modelRef'>
33
+ & Partial<Omit<Element<A>, 'id' | 'title' | 'kind'>>
39
34
  >
40
35
 
41
- export function buildComputedNodesFromElements(elements: ReadonlyArray<Element>, groups?: NodesGroup[]) {
42
- return buildComputedNodes(elements.map(modelElementAsNodeSource), groups)
36
+ export function elementModelToNodeSource<A extends AnyAux>(el: ElementModel<A>): ComputedNodeSource<A> {
37
+ return {
38
+ ...el.$element,
39
+ tags: [...el.tags],
40
+ id: el.id as scalar.NodeId,
41
+ modelRef: el.id,
42
+ }
43
43
  }
44
44
 
45
- export function buildComputedNodes(
46
- elements: ReadonlyArray<ComputedNodeSource>,
47
- groups?: ReadonlyArray<NodesGroup>,
48
- ): ReadonlyMap<Fqn, ComputedNode> {
49
- const nodesMap = new Map<Fqn, ComputedNode>()
45
+ export function buildComputedNodes<A extends AnyAux>(
46
+ elements: ReadonlyArray<ComputedNodeSource<A>>,
47
+ groups?: ReadonlyArray<NodesGroup<A>>,
48
+ ): ReadonlyMap<scalar.NodeId, ComputedNode<A>> {
49
+ const nodesMap = new Map<scalar.NodeId, ComputedNode<A>>()
50
50
 
51
- const elementToGroup = new Map<Fqn, NodeId>()
51
+ const elementToGroup = new Map<aux.StrictFqn<A>, scalar.NodeId>()
52
52
 
53
53
  groups?.forEach(({ id, parent, viewRule, elements }) => {
54
54
  if (parent) {
@@ -57,8 +57,10 @@ export function buildComputedNodes(
57
57
  nodesMap.set(id, {
58
58
  id,
59
59
  parent,
60
- kind: ElementKind.Group,
60
+ kind: GroupElementKind,
61
61
  title: viewRule.title ?? '',
62
+ description: null,
63
+ technology: null,
62
64
  color: viewRule.color ?? 'muted',
63
65
  shape: 'rectangle',
64
66
  children: [],
@@ -66,10 +68,7 @@ export function buildComputedNodes(
66
68
  outEdges: [],
67
69
  level: 0,
68
70
  depth: 0,
69
- description: null,
70
- technology: null,
71
- tags: null,
72
- links: null,
71
+ tags: [],
73
72
  style: {
74
73
  border: viewRule.border ?? 'dashed',
75
74
  opacity: viewRule.opacity ?? 0,
@@ -87,10 +86,10 @@ export function buildComputedNodes(
87
86
  // Ensure that parent nodes are created before child nodes
88
87
  Array.from(elements)
89
88
  .sort(compareByFqnHierarchically)
90
- .forEach(({ id, style, kind, title, color, shape, ...el }) => {
89
+ .forEach(({ id, style, kind, title, color, shape, tags, ...el }) => {
91
90
  let parent = parentFqn(id)
92
91
  let level = 0
93
- let parentNd: ComputedNode | undefined
92
+ let parentNd: ComputedNode<A> | undefined
94
93
  // Find the first ancestor that is already in the map
95
94
  while (parent) {
96
95
  parentNd = nodesMap.get(parent)
@@ -99,10 +98,12 @@ export function buildComputedNodes(
99
98
  }
100
99
  parent = parentFqn(parent)
101
100
  }
101
+ const fqn = el.modelRef ?? id as unknown as aux.StrictFqn<A>
102
102
  // If parent is not found in the map, check if it is in a group
103
- if (!parentNd && elementToGroup.has(id)) {
104
- parent = elementToGroup.get(id)!
105
- parentNd = nodesMap.get(parent)!
103
+ if (!parentNd && elementToGroup.has(fqn)) {
104
+ const parentGroupId = nonNullable(elementToGroup.get(fqn))
105
+ parentNd = nodesMap.get(parentGroupId)
106
+ parent = parentGroupId
106
107
  }
107
108
  if (parentNd) {
108
109
  // if parent has no children and we are about to add first one
@@ -115,18 +116,17 @@ export function buildComputedNodes(
115
116
  parentNd.children.push(id)
116
117
  level = parentNd.level + 1
117
118
  }
118
- const node: ComputedNode = {
119
+ const node: ComputedNode<A> = {
119
120
  id,
120
121
  parent,
121
122
  kind,
122
123
  title,
124
+ description: null,
125
+ technology: null,
123
126
  level,
124
127
  color: color ?? DefaultThemeColor,
125
128
  shape: shape ?? DefaultElementShape,
126
- description: null,
127
- technology: null,
128
- tags: null,
129
- links: null,
129
+ tags: tags ?? [],
130
130
  children: [],
131
131
  inEdges: [],
132
132
  outEdges: [],
@@ -139,7 +139,7 @@ export function buildComputedNodes(
139
139
  })
140
140
 
141
141
  // Create new map and add elements in the same order as they were in the input
142
- const orderedMap = new Map<Fqn, ComputedNode>()
142
+ const orderedMap = new Map<aux.NodeId, ComputedNode<A>>()
143
143
 
144
144
  groups?.forEach(({ id }) => {
145
145
  orderedMap.set(id, nonNullable(nodesMap.get(id)))
@@ -1,5 +1,5 @@
1
1
  import { entries, flatMap, groupBy, map, mapValues, pipe, piped, prop, sortBy, unique } from 'remeda'
2
- import type { ComputedNode, ElementNotation } from '../../types'
2
+ import type { ComputedNode, NodeNotation } from '../../types'
3
3
 
4
4
  /**
5
5
  * Build element notations from computed nodes:
@@ -9,7 +9,7 @@ import type { ComputedNode, ElementNotation } from '../../types'
9
9
  * 4. For each group get unique kinds
10
10
  * 5. Unwind the groups
11
11
  */
12
- export function buildElementNotations(nodes: ComputedNode[]): ElementNotation[] {
12
+ export function buildElementNotations(nodes: ComputedNode<any>[]): NodeNotation[] {
13
13
  return pipe(
14
14
  nodes,
15
15
  groupBy(prop('notation')),
@@ -22,25 +22,25 @@ export function buildElementNotations(nodes: ComputedNode[]): ElementNotation[]
22
22
  mapValues(
23
23
  piped(
24
24
  map(prop('kind')),
25
- unique()
26
- )
25
+ unique(),
26
+ ),
27
27
  ),
28
28
  entries(),
29
29
  map(([color, kinds]) => ({
30
30
  kinds,
31
- color
32
- }))
33
- )
31
+ color,
32
+ })),
33
+ ),
34
34
  ),
35
35
  entries(),
36
36
  flatMap(([shape, colors]) =>
37
37
  colors.map(({ color, kinds }) => ({
38
38
  shape,
39
39
  color,
40
- kinds
40
+ kinds,
41
41
  }))
42
- )
43
- )
42
+ ),
43
+ ),
44
44
  ),
45
45
  entries(),
46
46
  flatMap(([title, shapes]) =>
@@ -48,7 +48,7 @@ export function buildElementNotations(nodes: ComputedNode[]): ElementNotation[]
48
48
  title,
49
49
  shape,
50
50
  color,
51
- kinds
51
+ kinds,
52
52
  }))
53
53
  ),
54
54
  sortBy(
@@ -56,8 +56,8 @@ export function buildElementNotations(nodes: ComputedNode[]): ElementNotation[]
56
56
  prop('title'),
57
57
  [
58
58
  n => n.kinds.length,
59
- 'desc'
60
- ]
61
- )
59
+ 'desc',
60
+ ],
61
+ ),
62
62
  )
63
63
  }
@@ -1,37 +1,36 @@
1
- import { isNullish } from 'remeda'
2
- import { nonexhaustive } from '../../errors'
3
1
  import {
4
- ModelLayer,
2
+ FqnRef,
3
+ ModelFqnExpr,
5
4
  whereOperatorAsPredicate,
6
5
  } from '../../types'
7
- import { parentFqn } from '../../utils'
6
+ import { nonexhaustive, parentFqn } from '../../utils'
8
7
 
9
8
  type Predicate<T> = (x: T) => boolean
10
9
 
11
- export function elementExprToPredicate<T extends { id: string; tags?: readonly string[] | null; kind: string }>(
12
- target: ModelLayer.AnyFqnExpr,
10
+ export function elementExprToPredicate<T extends { id: string; tags: readonly string[]; kind: string }>(
11
+ target: ModelFqnExpr.Any,
13
12
  ): Predicate<T> {
14
- if (ModelLayer.FqnExpr.isCustom(target)) {
13
+ if (ModelFqnExpr.isCustom(target)) {
15
14
  return elementExprToPredicate(target.custom.expr)
16
15
  }
17
- if (ModelLayer.FqnExpr.isWhere(target)) {
16
+ if (ModelFqnExpr.isWhere(target)) {
18
17
  const predicate = elementExprToPredicate(target.where.expr)
19
18
  const where = whereOperatorAsPredicate(target.where.condition)
20
19
  return n => predicate(n) && where(n)
21
20
  }
22
- if (ModelLayer.FqnExpr.isElementKindExpr(target)) {
21
+ if (ModelFqnExpr.isElementKindExpr(target)) {
23
22
  return target.isEqual ? n => n.kind === target.elementKind : n => n.kind !== target.elementKind
24
23
  }
25
- if (ModelLayer.FqnExpr.isElementTagExpr(target)) {
24
+ if (ModelFqnExpr.isElementTagExpr(target)) {
26
25
  return target.isEqual
27
- ? ({ tags }) => !!tags && tags.includes(target.elementTag)
28
- : ({ tags }) => isNullish(tags) || !tags.includes(target.elementTag)
26
+ ? ({ tags }) => tags.includes(target.elementTag)
27
+ : ({ tags }) => !tags.includes(target.elementTag)
29
28
  }
30
- if (ModelLayer.FqnExpr.isWildcard(target)) {
29
+ if (ModelFqnExpr.isWildcard(target)) {
31
30
  return () => true
32
31
  }
33
- if (ModelLayer.FqnExpr.isModelRef(target)) {
34
- const fqn = ModelLayer.FqnRef.toFqn(target.ref)
32
+ if (ModelFqnExpr.isModelRef(target)) {
33
+ const fqn = FqnRef.flatten(target.ref)
35
34
  if (target.selector === 'expanded') {
36
35
  return (n) => {
37
36
  return n.id === fqn || parentFqn(n.id) === fqn
@@ -1,7 +1,6 @@
1
1
  import { last, reverse } from 'remeda'
2
- import { invariant } from '../../errors'
3
- import type { Fqn } from '../../types'
4
- import type { ComputedEdge, ComputedNode } from '../../types/view'
2
+ import type { AnyAux, ComputedEdge, ComputedNode } from '../../types'
3
+ import { invariant } from '../../utils'
5
4
  import { commonHead } from '../../utils/common-head'
6
5
  import { ancestorsOfNode } from './ancestorsOfNode'
7
6
 
@@ -9,7 +8,10 @@ import { ancestorsOfNode } from './ancestorsOfNode'
9
8
  * Update `inEdges` and `outEdges` props of nodes based on the edges
10
9
  * Mutates nodes and updates their in/out edges
11
10
  */
12
- export function linkNodesWithEdges(nodesMap: ReadonlyMap<Fqn, ComputedNode>, edges: ComputedEdge[]) {
11
+ export function linkNodesWithEdges<A extends AnyAux>(
12
+ nodesMap: ReadonlyMap<any, ComputedNode<A>>,
13
+ edges: ComputedEdge<A>[],
14
+ ) {
13
15
  for (const edge of edges) {
14
16
  const source = nodesMap.get(edge.source)
15
17
  const target = nodesMap.get(edge.target)
@@ -1,9 +1,28 @@
1
1
  import { isTruthy, only, pickBy, pipe, reduce, unique } from 'remeda'
2
- import type { Color, DeploymentRelation, Link, Tag, ViewId } from '../../types'
3
- import type { ModelRelation, RelationshipArrowType, RelationshipKind, RelationshipLineType } from '../../types/relation'
2
+ import type {
3
+ AnyAux,
4
+ aux,
5
+ Color,
6
+ DeploymentRelationship,
7
+ Link,
8
+ NonEmptyArray,
9
+ Relationship,
10
+ RelationshipArrowType,
11
+ RelationshipLineType,
12
+ } from '../../types'
4
13
  import { isNonEmptyArray } from '../../utils'
5
14
 
6
- function pickRelationshipProps(relation: ModelRelation | DeploymentRelation) {
15
+ function pickRelationshipProps<A extends AnyAux>(relation: Relationship<A> | DeploymentRelationship<A>): {
16
+ title?: string
17
+ description?: string | null
18
+ technology: string | null
19
+ kind: aux.RelationKind<A> | null
20
+ color: Color | null
21
+ line: RelationshipLineType | null
22
+ head: RelationshipArrowType | null
23
+ tail: RelationshipArrowType | null
24
+ navigateTo: aux.StrictViewId<A> | null
25
+ } {
7
26
  const {
8
27
  title,
9
28
  description = null,
@@ -13,33 +32,47 @@ function pickRelationshipProps(relation: ModelRelation | DeploymentRelation) {
13
32
  line = null,
14
33
  head = null,
15
34
  tail = null,
16
- navigateTo = null
35
+ navigateTo = null,
17
36
  } = relation
18
37
  return {
19
38
  // Pick description only if title is present
20
39
  ...(title && {
21
40
  title,
22
- description
41
+ description,
23
42
  }),
24
43
  technology,
25
- kind,
44
+ kind: kind as aux.RelationKind<A> | null,
26
45
  color,
27
46
  line,
28
47
  head,
29
48
  tail,
30
- navigateTo
49
+ navigateTo: navigateTo as aux.StrictViewId<A> | null,
31
50
  }
32
51
  }
33
52
 
53
+ export type MergedRelationshipProps<A extends AnyAux> = {
54
+ title?: string | null
55
+ description?: string
56
+ technology?: string
57
+ kind?: aux.RelationKind<A>
58
+ color?: Color
59
+ line?: RelationshipLineType
60
+ head?: RelationshipArrowType
61
+ tail?: RelationshipArrowType
62
+ navigateTo?: aux.StrictViewId<A>
63
+ tags?: NonEmptyArray<aux.Tag<A>>
64
+ links?: NonEmptyArray<Link>
65
+ }
66
+
34
67
  /**
35
68
  * Merges properties from multiple relationships into a single object.
36
69
  * @param relations - The relationships to merge.
37
70
  * @param prefer - The relationship to prefer when merging.
38
71
  */
39
- export function mergePropsFromRelationships(
40
- relations: Array<ModelRelation | DeploymentRelation>,
41
- prefer?: ModelRelation | DeploymentRelation
42
- ) {
72
+ export function mergePropsFromRelationships<A extends AnyAux>(
73
+ relations: Array<Relationship<A> | DeploymentRelationship<A>>,
74
+ prefer?: Relationship<A> | DeploymentRelationship<A>,
75
+ ): MergedRelationshipProps<A> {
43
76
  const allprops = pipe(
44
77
  relations,
45
78
  reduce(
@@ -83,16 +116,16 @@ export function mergePropsFromRelationships(
83
116
  title: [] as string[],
84
117
  description: [] as string[],
85
118
  technology: [] as string[],
86
- kind: [] as RelationshipKind[],
119
+ kind: [] as aux.RelationKind<A>[],
87
120
  head: [] as RelationshipArrowType[],
88
121
  tail: [] as RelationshipArrowType[],
89
122
  color: [] as Color[],
90
- tags: [] as Tag[],
123
+ tags: [] as aux.Tag<A>[],
91
124
  links: [] as Link[],
92
125
  line: [] as RelationshipLineType[],
93
- navigateTo: [] as ViewId[]
94
- }
95
- )
126
+ navigateTo: [] as aux.ViewId<A>[],
127
+ },
128
+ ),
96
129
  )
97
130
 
98
131
  let technology = only(allprops.technology)
@@ -112,17 +145,16 @@ export function mergePropsFromRelationships(
112
145
  line: only(allprops.line),
113
146
  navigateTo: only(allprops.navigateTo),
114
147
  ...isNonEmptyArray(allprops.links) && { links: allprops.links },
115
- ...isNonEmptyArray(tags) && { tags }
148
+ ...isNonEmptyArray(tags) && { tags },
116
149
  },
117
- isTruthy
150
+ isTruthy,
118
151
  )
119
152
 
120
153
  if (prefer) {
121
154
  return {
122
155
  ...merged,
123
- ...pickBy(pickRelationshipProps(prefer), isTruthy)
156
+ ...pickBy(pickRelationshipProps(prefer), isTruthy),
124
157
  }
125
158
  }
126
159
  return merged
127
160
  }
128
- export type MergedRelationshipProps = ReturnType<typeof mergePropsFromRelationships>
@@ -1,35 +1,35 @@
1
- import { nonexhaustive } from '../../errors'
2
- import { ModelLayer } from '../../types'
3
- import { whereOperatorAsPredicate } from '../../types/operators'
4
- import type { ModelRelation } from '../../types/relation'
5
- import type { ComputedNode } from '../../types/view'
1
+ import type { AnyAux, aux, ComputedNode } from '../../types'
2
+ import { ModelRelationExpr, whereOperatorAsPredicate } from '../../types'
3
+ import { nonexhaustive } from '../../utils'
6
4
  import { elementExprToPredicate } from './elementExpressionToPredicate'
7
5
 
8
6
  type Predicate<T> = (x: T) => boolean
9
- export type FilterableEdge = Pick<ModelRelation, 'kind' | 'tags'> & {
10
- source: ComputedNode
11
- target: ComputedNode
7
+ export type FilterableEdge<A extends AnyAux> = {
8
+ tags?: aux.Tags<A> | null | undefined
9
+ kind?: string
10
+ source: ComputedNode<A>
11
+ target: ComputedNode<A>
12
12
  }
13
13
 
14
- export function relationExpressionToPredicates<T extends FilterableEdge>(
15
- expr: ModelLayer.AnyRelationExpr,
14
+ export function relationExpressionToPredicates<A extends AnyAux, T extends FilterableEdge<A>>(
15
+ expr: ModelRelationExpr.Any<A>,
16
16
  ): Predicate<T> {
17
17
  switch (true) {
18
- case ModelLayer.RelationExpr.isCustom(expr): {
18
+ case ModelRelationExpr.isCustom(expr): {
19
19
  return relationExpressionToPredicates(expr.customRelation.expr)
20
20
  }
21
- case ModelLayer.RelationExpr.isWhere(expr): {
21
+ case ModelRelationExpr.isWhere(expr): {
22
22
  const predicate = relationExpressionToPredicates(expr.where.expr)
23
23
  const where = whereOperatorAsPredicate(expr.where.condition)
24
24
  return e =>
25
25
  predicate(e) && where({
26
- source: { tags: e.source.tags, kind: e.source.kind },
27
- target: { tags: e.target.tags, kind: e.target.kind },
26
+ source: { tags: e.source.tags, kind: e.source.kind as aux.AllKinds<A> },
27
+ target: { tags: e.target.tags, kind: e.target.kind as aux.AllKinds<A> },
28
28
  ...(e.tags && { tags: e.tags }),
29
- ...(e.kind && { kind: e.kind }),
29
+ ...(e.kind && { kind: e.kind as aux.AllKinds<A> }),
30
30
  })
31
31
  }
32
- case ModelLayer.RelationExpr.isDirect(expr): {
32
+ case ModelRelationExpr.isDirect(expr): {
33
33
  const isSource = elementExprToPredicate(expr.source)
34
34
  const isTarget = elementExprToPredicate(expr.target)
35
35
  return edge => {
@@ -37,15 +37,15 @@ export function relationExpressionToPredicates<T extends FilterableEdge>(
37
37
  || (!!expr.isBidirectional && isSource(edge.target) && isTarget(edge.source))
38
38
  }
39
39
  }
40
- case ModelLayer.RelationExpr.isInOut(expr): {
40
+ case ModelRelationExpr.isInOut(expr): {
41
41
  const isInOut = elementExprToPredicate(expr.inout)
42
42
  return edge => isInOut(edge.source) || isInOut(edge.target)
43
43
  }
44
- case ModelLayer.RelationExpr.isIncoming(expr): {
44
+ case ModelRelationExpr.isIncoming(expr): {
45
45
  const isTarget = elementExprToPredicate(expr.incoming)
46
46
  return edge => isTarget(edge.target)
47
47
  }
48
- case ModelLayer.RelationExpr.isOutgoing(expr): {
48
+ case ModelRelationExpr.isOutgoing(expr): {
49
49
  const isSource = elementExprToPredicate(expr.outgoing)
50
50
  return edge => isSource(edge.source)
51
51
  }
@@ -1,21 +1,22 @@
1
1
  import { unique, values } from 'remeda'
2
- import { type ExtendsElementView, type LikeC4View, isElementView, isExtendsElementView } from '../../types/view'
2
+ import { type LikeC4View, isElementView, isExtendsElementView } from '../../types/view'
3
3
 
4
4
  import Graph from 'graphology'
5
5
  import { topologicalSort } from 'graphology-dag/topological-sort'
6
6
  import willCreateCycle from 'graphology-dag/will-create-cycle'
7
+ import type { AnyAux, aux, ParsedElementView } from '../../types'
7
8
  import { isNonEmptyArray } from '../../utils'
8
9
  /**
9
10
  * Resolve rules of extended views
10
11
  * (Removes invalid views)
11
12
  */
12
- export function resolveRulesExtendedViews<V extends Record<any, LikeC4View>>(
13
+ export function resolveRulesExtendedViews<A extends AnyAux, V extends Record<any, LikeC4View<A>>>(
13
14
  unresolvedViews: V,
14
15
  ): V {
15
- const g = new Graph<{ view: LikeC4View }>({
16
+ const g = new Graph<{ view: LikeC4View<A> }>({
16
17
  type: 'directed',
17
18
  })
18
- const extendedViews = [] as ExtendsElementView<any, any>[]
19
+ const extendedViews = [] as ParsedElementView<A>[]
19
20
  for (const view of values(unresolvedViews)) {
20
21
  g.addNode(view.id, { view })
21
22
  if (isExtendsElementView(view)) {
@@ -57,7 +58,7 @@ export function resolveRulesExtendedViews<V extends Record<any, LikeC4View>>(
57
58
  const tags = unique([
58
59
  ...(extendsFrom.tags ?? []),
59
60
  ...(view.tags ?? []),
60
- ])
61
+ ]) as aux.Tags<A>
61
62
 
62
63
  const links = [
63
64
  ...(extendsFrom.links ?? []),
@@ -69,12 +70,12 @@ export function resolveRulesExtendedViews<V extends Record<any, LikeC4View>>(
69
70
  ...view,
70
71
  title: view.title ?? extendsFrom.title ?? null,
71
72
  description: view.description ?? extendsFrom.description ?? null,
72
- tags: isNonEmptyArray(tags) ? tags : null,
73
+ tags,
73
74
  links: isNonEmptyArray(links) ? links : null,
74
75
  rules: [...extendsFrom.rules, ...view.rules],
75
76
  }
76
77
  return acc
77
- }, {} as Record<string, LikeC4View>) as V
78
+ }, {} as Record<string, LikeC4View<A>>) as V
78
79
 
79
80
  // forEachNodeInTopologicalOrder(g, (_id, { view }) => {
80
81
 
@@ -1,32 +1,37 @@
1
1
  import { isNullish } from 'remeda'
2
- import { nonexhaustive } from '../../errors'
3
2
  import {
4
- type DynamicView,
3
+ type AnyAux,
4
+ type DynamicViewIncludeRule,
5
5
  type DynamicViewRule,
6
- type ElementView,
6
+ type ElementViewPredicate,
7
+ type ElementViewRule,
8
+ type ModelGlobals,
9
+ type ParsedDynamicView,
10
+ type ParsedElementView,
11
+ type ViewRuleGlobalPredicateRef,
12
+ type ViewRuleGlobalStyle,
7
13
  isDynamicView,
8
14
  isElementView,
9
15
  isViewRuleGlobalPredicateRef,
10
16
  isViewRuleGlobalStyle,
11
- type ModelGlobals,
12
- type ViewRule,
13
- type ViewRuleGlobalPredicateRef,
14
- type ViewRuleGlobalStyle
15
17
  } from '../../types'
18
+ import { nonexhaustive } from '../../utils'
16
19
 
17
- export function resolveGlobalRules<V extends DynamicView | ElementView>(
18
- view: V,
19
- globals: ModelGlobals
20
- ): V {
20
+ export function resolveGlobalRules<A extends AnyAux>(
21
+ view: ParsedElementView<A> | ParsedDynamicView<A>,
22
+ globals: ModelGlobals<A>,
23
+ ) {
21
24
  if (isElementView(view)) {
22
25
  return {
23
26
  ...view,
24
- rules: resolveGlobalRulesInElementView(view.rules, globals)
27
+ rules: resolveGlobalRulesInElementView(view.rules, globals),
25
28
  }
26
- } else if (isDynamicView(view)) {
29
+ }
30
+
31
+ if (isDynamicView(view)) {
27
32
  return {
28
33
  ...view,
29
- rules: resolveGlobalRulesInDynamicView(view.rules, globals)
34
+ rules: resolveGlobalRulesInDynamicView(view.rules, globals),
30
35
  }
31
36
  }
32
37
  nonexhaustive(view)
@@ -34,17 +39,17 @@ export function resolveGlobalRules<V extends DynamicView | ElementView>(
34
39
 
35
40
  type ViewRuleGlobal = ViewRuleGlobalPredicateRef | ViewRuleGlobalStyle
36
41
 
37
- export function resolveGlobalRulesInElementView(
38
- rules: ViewRule[],
39
- globals: ModelGlobals
40
- ): Array<Exclude<ViewRule, ViewRuleGlobal>> {
42
+ export function resolveGlobalRulesInElementView<M extends AnyAux>(
43
+ rules: ElementViewRule<M>[],
44
+ globals: ModelGlobals<M>,
45
+ ): Array<Exclude<ElementViewRule<M>, ViewRuleGlobal>> {
41
46
  return rules.reduce((acc, rule) => {
42
47
  if (isViewRuleGlobalPredicateRef(rule)) {
43
48
  const globalPredicates = globals.predicates[rule.predicateId]
44
49
  if (isNullish(globalPredicates)) {
45
50
  return acc
46
51
  }
47
- return acc.concat(globalPredicates)
52
+ return acc.concat(globalPredicates as ElementViewPredicate<M>[])
48
53
  }
49
54
  if (isViewRuleGlobalStyle(rule)) {
50
55
  const globalStyles = globals.styles[rule.styleId]
@@ -55,20 +60,20 @@ export function resolveGlobalRulesInElementView(
55
60
  }
56
61
  acc.push(rule)
57
62
  return acc
58
- }, [] as Array<Exclude<ViewRule, ViewRuleGlobal>>)
63
+ }, [] as Array<Exclude<ElementViewRule<M>, ViewRuleGlobal>>)
59
64
  }
60
65
 
61
- export function resolveGlobalRulesInDynamicView(
62
- rules: DynamicViewRule[],
63
- globals: ModelGlobals
64
- ): Array<Exclude<DynamicViewRule, ViewRuleGlobal>> {
66
+ export function resolveGlobalRulesInDynamicView<M extends AnyAux>(
67
+ rules: DynamicViewRule<M>[],
68
+ globals: ModelGlobals<M>,
69
+ ): Array<Exclude<DynamicViewRule<M>, ViewRuleGlobal>> {
65
70
  return rules.reduce((acc, rule) => {
66
71
  if (isViewRuleGlobalPredicateRef(rule)) {
67
72
  const globalPredicates = globals.dynamicPredicates[rule.predicateId]
68
73
  if (isNullish(globalPredicates)) {
69
74
  return acc
70
75
  }
71
- return acc.concat(globalPredicates)
76
+ return acc.concat(globalPredicates as DynamicViewIncludeRule<M>[])
72
77
  }
73
78
  if (isViewRuleGlobalStyle(rule)) {
74
79
  const globalStyles = globals.styles[rule.styleId]
@@ -79,5 +84,5 @@ export function resolveGlobalRulesInDynamicView(
79
84
  }
80
85
  acc.push(rule)
81
86
  return acc
82
- }, [] as Array<Exclude<DynamicViewRule, ViewRuleGlobal>>)
87
+ }, [] as Array<Exclude<DynamicViewRule<M>, ViewRuleGlobal>>)
83
88
  }