@likec4/core 1.17.1 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/dist/compute-view/index.d.mts +91 -0
  2. package/dist/compute-view/index.d.ts +91 -0
  3. package/dist/compute-view/index.mjs +79 -0
  4. package/dist/index.d.mts +246 -434
  5. package/dist/index.d.ts +246 -434
  6. package/dist/index.mjs +418 -1322
  7. package/dist/model/index.d.mts +642 -0
  8. package/dist/model/index.d.ts +642 -0
  9. package/dist/model/index.mjs +2 -0
  10. package/dist/shared/core.BBgiAAv1.mjs +144 -0
  11. package/dist/shared/core.BPyJvuMU.mjs +2420 -0
  12. package/dist/shared/core.BQ4AbfvB.mjs +9228 -0
  13. package/dist/shared/{core.CoLQkcYL.d.mts → core.DMYY7C8s.d.mts} +398 -249
  14. package/dist/shared/{core.CoLQkcYL.d.ts → core.DMYY7C8s.d.ts} +398 -249
  15. package/dist/shared/core.k12s_keD.mjs +203 -0
  16. package/dist/types/index.d.mts +69 -1
  17. package/dist/types/index.d.ts +69 -1
  18. package/dist/types/index.mjs +21 -1
  19. package/package.json +38 -40
  20. package/src/builder/Builder.deployment.ts +219 -0
  21. package/src/builder/Builder.deploymentModel.ts +360 -0
  22. package/src/builder/Builder.element.ts +17 -19
  23. package/src/builder/Builder.model.ts +13 -2
  24. package/src/builder/Builder.test-d.ts +59 -4
  25. package/src/builder/Builder.ts +238 -53
  26. package/src/builder/Builder.view.ts +238 -53
  27. package/src/builder/_types.ts +87 -15
  28. package/src/compute-view/LikeC4ModelGraph.ts +330 -0
  29. package/src/compute-view/compute-view.ts +92 -0
  30. package/src/compute-view/deployment-view/Memory.ts +108 -0
  31. package/src/compute-view/deployment-view/Stage.ts +145 -0
  32. package/src/compute-view/deployment-view/__test__/fixture.ts +210 -0
  33. package/src/compute-view/deployment-view/_types.ts +29 -0
  34. package/src/compute-view/deployment-view/clean-connections.ts +229 -0
  35. package/src/compute-view/deployment-view/compute.ts +111 -0
  36. package/src/compute-view/deployment-view/predicates/deployment-ref.ts +146 -0
  37. package/src/compute-view/deployment-view/predicates/relation-direct.ts +130 -0
  38. package/src/compute-view/deployment-view/predicates/relation-in-out.ts +43 -0
  39. package/src/compute-view/deployment-view/predicates/relation-incoming.ts +46 -0
  40. package/src/compute-view/deployment-view/predicates/relation-outgoing.ts +49 -0
  41. package/src/compute-view/deployment-view/predicates/wildcard.ts +29 -0
  42. package/src/compute-view/deployment-view/utils.ts +212 -0
  43. package/src/compute-view/dynamic-view/__test__/fixture.ts +58 -0
  44. package/src/compute-view/dynamic-view/compute.ts +310 -0
  45. package/src/compute-view/element-view/__test__/__snapshots__/legacy.spec.ts.snap +456 -0
  46. package/src/compute-view/element-view/__test__/fixture.ts +645 -0
  47. package/src/compute-view/element-view/compute.ts +734 -0
  48. package/src/compute-view/element-view/predicates.ts +517 -0
  49. package/src/compute-view/index.ts +5 -0
  50. package/src/compute-view/utils/__snapshots__/sortNodes.spec.ts.snap +113 -0
  51. package/src/compute-view/utils/ancestorsOfNode.ts +19 -0
  52. package/src/compute-view/utils/applyCustomElementProperties.ts +66 -0
  53. package/src/compute-view/utils/applyCustomRelationProperties.ts +41 -0
  54. package/src/compute-view/utils/applyViewRuleStyles.ts +53 -0
  55. package/src/compute-view/utils/buildComputedNodes.ts +148 -0
  56. package/src/compute-view/utils/buildElementNotations.ts +63 -0
  57. package/src/compute-view/utils/derive-edge-props-from-relationships.ts +97 -0
  58. package/src/compute-view/utils/elementExpressionToPredicate.ts +52 -0
  59. package/src/compute-view/utils/link-nodes-with-edges.ts +58 -0
  60. package/src/compute-view/utils/relationExpressionToPredicates.ts +55 -0
  61. package/src/compute-view/utils/resolve-extended-views.ts +65 -0
  62. package/src/compute-view/utils/resolve-global-rules.ts +83 -0
  63. package/src/compute-view/utils/sortNodes.ts +101 -0
  64. package/src/compute-view/utils/topological-sort.ts +160 -0
  65. package/src/compute-view/utils/uniqueTags.test.ts +42 -0
  66. package/src/compute-view/utils/uniqueTags.ts +21 -0
  67. package/src/compute-view/utils/view-hash.ts +27 -0
  68. package/src/compute-view/utils/with-readable-edges.ts +31 -0
  69. package/src/index.ts +5 -1
  70. package/src/model/DeploymentElementModel.ts +534 -0
  71. package/src/model/DeploymentModel.ts +364 -0
  72. package/src/model/ElementModel.ts +195 -0
  73. package/src/model/LikeC4Model.test-d.ts +151 -0
  74. package/src/model/LikeC4Model.ts +280 -457
  75. package/src/model/RelationModel.ts +84 -0
  76. package/src/model/__test__/fixture.ts +153 -338
  77. package/src/model/connection/ConnectionModel.ts +94 -0
  78. package/src/model/connection/DeploymentConnectionModel.ts +99 -0
  79. package/src/model/connection/deployment.ts +124 -0
  80. package/src/model/connection/model.ts +123 -0
  81. package/src/model/index.ts +4 -2
  82. package/src/model/types.ts +95 -15
  83. package/src/model/view/EdgeModel.ts +103 -0
  84. package/src/model/view/LikeC4ViewModel.ts +220 -0
  85. package/src/model/view/NodeModel.ts +228 -0
  86. package/src/types/_common.ts +2 -0
  87. package/src/types/deployments.ts +237 -0
  88. package/src/types/element.ts +7 -3
  89. package/src/types/expression.ts +8 -6
  90. package/src/types/index.ts +1 -0
  91. package/src/types/model.ts +68 -9
  92. package/src/types/overview-graph.ts +2 -2
  93. package/src/types/relation.ts +4 -4
  94. package/src/types/view-notation.ts +2 -2
  95. package/src/types/view.ts +104 -30
  96. package/src/utils/{commonHead.ts → common-head.ts} +2 -0
  97. package/src/utils/fqn.ts +83 -27
  98. package/src/utils/getOrCreate.ts +42 -0
  99. package/src/utils/graphlib.ts +10 -0
  100. package/src/utils/index.ts +4 -1
  101. package/src/utils/object-hash.ts +3 -0
  102. package/src/utils/set.ts +76 -0
  103. package/src/utils/string-hash.ts +16 -0
  104. package/dist/index.cjs +0 -5452
  105. package/dist/index.d.cts +0 -1053
  106. package/dist/shared/core.BXTs-8n7.mjs +0 -293
  107. package/dist/shared/core.CoLQkcYL.d.cts +0 -734
  108. package/dist/shared/core.DIYZvpVg.cjs +0 -353
  109. package/dist/types/index.cjs +0 -70
  110. package/dist/types/index.d.cts +0 -2
  111. package/src/model/LikeC4DiagramModel.ts +0 -338
  112. package/src/model/LikeC4ViewModel.ts +0 -320
@@ -0,0 +1,99 @@
1
+ import { invariant } from '../../errors'
2
+ import { stringHash } from '../../utils'
3
+ import { equals } from '../../utils/set'
4
+ import {
5
+ type DeploymentElementModel,
6
+ DeploymentNodeModel,
7
+ type DeploymentRelationModel,
8
+ RelationshipsAccum
9
+ } from '../DeploymentElementModel'
10
+ import type { RelationshipModel } from '../RelationModel'
11
+ import type { AnyAux, IteratorLike } from '../types'
12
+ import type { Connection } from './ConnectionModel'
13
+
14
+ /**
15
+ * Connection is ephemeral entity, result of a resolving relationships between source and target.
16
+ * Includes direct relationships and/or between their nested elements.
17
+ */
18
+ export class DeploymentConnectionModel<M extends AnyAux = AnyAux>
19
+ implements Connection<DeploymentElementModel<M>, M['EdgeId']>
20
+ {
21
+ /**
22
+ * Common ancestor of the source and target elements.
23
+ * Represents the boundary of the connection.
24
+ */
25
+ readonly boundary: DeploymentNodeModel<M> | null
26
+
27
+ constructor(
28
+ public readonly source: DeploymentElementModel<M>,
29
+ public readonly target: DeploymentElementModel<M>,
30
+ public readonly relations: RelationshipsAccum<M>
31
+ ) {
32
+ this.id = stringHash(`deployment:${source.id}:${target.id}`) as M['EdgeId']
33
+ this.boundary = source.commonAncestor(target)
34
+ }
35
+
36
+ readonly id: M['EdgeId']
37
+
38
+ /**
39
+ * Human readable expression of the connection
40
+ * Mostly used for testing and debugging
41
+ */
42
+ get expression(): string {
43
+ return `${this.source.id} -> ${this.target.id}`
44
+ }
45
+
46
+ nonEmpty(): boolean {
47
+ return this.relations.nonEmpty
48
+ }
49
+
50
+ /**
51
+ * Check if connection contains deployment relation,
52
+ * that is directly connected to source or target.
53
+ */
54
+ public hasDirectDeploymentRelation(): boolean {
55
+ for (const relation of this.relations.deployment) {
56
+ if (relation.source.id === this.source.id || relation.target.id === this.target.id) {
57
+ return true
58
+ }
59
+ }
60
+ return false
61
+ }
62
+
63
+ public *values(): IteratorLike<RelationshipModel<M> | DeploymentRelationModel<M>> {
64
+ yield* this.relations.model
65
+ yield* this.relations.deployment
66
+ }
67
+
68
+ /**
69
+ * Merge with another connection, if it has the same source and target.
70
+ * Returns new connection with union of relationships.
71
+ */
72
+ public mergeWith(other: DeploymentConnectionModel<M>): DeploymentConnectionModel<M> {
73
+ invariant(this.source.id === other.source.id, 'Cannot merge connections with different sources')
74
+ invariant(this.target.id === other.target.id, 'Cannot merge connections with different targets')
75
+ return new DeploymentConnectionModel(
76
+ this.source,
77
+ this.target,
78
+ this.relations.union(other.relations)
79
+ )
80
+ }
81
+
82
+ public difference(other: DeploymentConnectionModel<M>): DeploymentConnectionModel<M> {
83
+ invariant(this.source.id === other.source.id, 'Cannot difference connection with different sources')
84
+ invariant(this.target.id === other.target.id, 'Cannot difference connection with different targets')
85
+ return new DeploymentConnectionModel(
86
+ this.source,
87
+ this.target,
88
+ this.relations.difference(other.relations)
89
+ )
90
+ }
91
+
92
+ public equals(other: DeploymentConnectionModel<M>): boolean {
93
+ return this.id === other.id
94
+ && this.source.id === other.source.id
95
+ && this.target.id === other.target.id
96
+ && equals(this.relations.model, other.relations.model)
97
+ && equals(this.relations.deployment, other.relations.deployment)
98
+ }
99
+ }
@@ -0,0 +1,124 @@
1
+ import { isSameHierarchy } from '../../utils/fqn'
2
+ import { type DeploymentElementModel } from '../DeploymentElementModel'
3
+ import type { AnyAux } from '../types'
4
+ import { DeploymentConnectionModel } from './DeploymentConnectionModel'
5
+
6
+ export { mergeConnections } from './model'
7
+
8
+ export { DeploymentConnectionModel }
9
+
10
+ /**
11
+ * Resolve connection from source to target
12
+ * If direction is `both`, also look for reverse connection
13
+ *
14
+ * @default direction directed
15
+ */
16
+ export function findConnection<M extends AnyAux>(
17
+ source: DeploymentElementModel<M>,
18
+ target: DeploymentElementModel<NoInfer<M>>,
19
+ direction: 'directed'
20
+ ):
21
+ | readonly [DeploymentConnectionModel<M>]
22
+ | readonly []
23
+ export function findConnection<M extends AnyAux>(
24
+ source: DeploymentElementModel<M>,
25
+ target: DeploymentElementModel<NoInfer<M>>,
26
+ direction: 'both'
27
+ ):
28
+ | readonly [DeploymentConnectionModel<M>, DeploymentConnectionModel<M>]
29
+ | readonly [DeploymentConnectionModel<M>]
30
+ | readonly []
31
+ export function findConnection<M extends AnyAux>(
32
+ source: DeploymentElementModel<M>,
33
+ target: DeploymentElementModel<NoInfer<M>>,
34
+ direction?: 'directed' | 'both'
35
+ ):
36
+ | readonly [DeploymentConnectionModel<M>, DeploymentConnectionModel<M>]
37
+ | readonly [DeploymentConnectionModel<M>]
38
+ | readonly []
39
+ export function findConnection<M extends AnyAux>(
40
+ source: DeploymentElementModel<M>,
41
+ target: DeploymentElementModel<NoInfer<M>>,
42
+ direction: 'directed' | 'both' = 'directed'
43
+ ) {
44
+ if (source === target) {
45
+ return []
46
+ }
47
+ if (isSameHierarchy(source, target)) {
48
+ return []
49
+ }
50
+ const directedIntersection = source.allOutgoing.intersect(target.allIncoming)
51
+
52
+ const directed = directedIntersection.nonEmpty
53
+ ? [
54
+ new DeploymentConnectionModel<M>(
55
+ source,
56
+ target,
57
+ directedIntersection
58
+ )
59
+ ] as const
60
+ : [] as const
61
+
62
+ if (direction === 'directed') {
63
+ return directed
64
+ }
65
+ return [
66
+ ...directed,
67
+ ...findConnection(target, source, 'directed')
68
+ ] as const
69
+ }
70
+
71
+ /**
72
+ * Resolve all connections between element and others
73
+ * By default, look for both directions.
74
+ *
75
+ * @default direction both
76
+ */
77
+ export function findConnectionsBetween<M extends AnyAux>(
78
+ element: DeploymentElementModel<M>,
79
+ others: Iterable<DeploymentElementModel<NoInfer<M>>>,
80
+ direction: 'directed' | 'both' = 'both'
81
+ ): readonly DeploymentConnectionModel<M>[] {
82
+ if (element.allIncoming.isEmpty && element.allOutgoing.isEmpty) {
83
+ return []
84
+ }
85
+
86
+ // We separate resolved connection,
87
+ // because we want return outgoing first
88
+ const outgoing = [] as DeploymentConnectionModel<M>[]
89
+ const incoming = [] as DeploymentConnectionModel<M>[]
90
+ for (const _other of others) {
91
+ if (element === _other) {
92
+ continue
93
+ }
94
+ for (const found of findConnection(element, _other, direction)) {
95
+ if (found.source === element) {
96
+ outgoing.push(found)
97
+ } else {
98
+ incoming.push(found)
99
+ }
100
+ }
101
+ }
102
+ return [
103
+ ...outgoing,
104
+ ...incoming
105
+ ]
106
+ }
107
+
108
+ /**
109
+ * Resolve all connections within a given set of elements
110
+ */
111
+ export function findConnectionsWithin<M extends AnyAux>(
112
+ elements: Iterable<DeploymentElementModel<M>>
113
+ ): readonly DeploymentConnectionModel<M>[] {
114
+ return [...elements].reduce((acc, el, index, array) => {
115
+ // skip for last element
116
+ if (index === array.length - 1) {
117
+ return acc
118
+ }
119
+ acc.push(
120
+ ...findConnectionsBetween(el, array.slice(index + 1), 'both')
121
+ )
122
+ return acc
123
+ }, [] as DeploymentConnectionModel<M>[])
124
+ }
@@ -0,0 +1,123 @@
1
+ import { isSameHierarchy } from '../../utils/fqn'
2
+ import { intersection } from '../../utils/set'
3
+ import type { ElementModel } from '../ElementModel'
4
+ import type { AnyAux } from '../types'
5
+ import { type Connection, ConnectionModel } from './ConnectionModel'
6
+
7
+ export function mergeConnections<C extends Connection<any, any>>(
8
+ connections: readonly C[]
9
+ ): C[] {
10
+ const map = new Map<C['id'], C>()
11
+ for (const conn of connections) {
12
+ const existing = map.get(conn.id)
13
+ if (existing) {
14
+ map.set(conn.id, existing.mergeWith(conn) as any)
15
+ } else {
16
+ map.set(conn.id, conn)
17
+ }
18
+ }
19
+ return [...map.values()]
20
+ }
21
+
22
+ /**
23
+ * Resolve connection from source to target
24
+ * If direction is `both`, also look for reverse connection
25
+ */
26
+ export function findConnection<M extends AnyAux>(
27
+ source: ElementModel<M>,
28
+ target: ElementModel<NoInfer<M>>,
29
+ direction: 'directed' | 'both' = 'directed'
30
+ ):
31
+ | readonly [ConnectionModel<M>, ConnectionModel<M>]
32
+ | readonly [ConnectionModel<M>]
33
+ | readonly []
34
+ {
35
+ if (source === target) {
36
+ return []
37
+ }
38
+ if (isSameHierarchy(source, target)) {
39
+ return []
40
+ }
41
+
42
+ const directedIntersection = intersection(source.allOutgoing, target.allIncoming)
43
+
44
+ const directed = directedIntersection.size > 0
45
+ ? [
46
+ new ConnectionModel(
47
+ source,
48
+ target,
49
+ directedIntersection
50
+ )
51
+ ] as const
52
+ : [] as const
53
+
54
+ if (direction === 'directed') {
55
+ return directed
56
+ }
57
+
58
+ const reverseIntersection = intersection(source.allIncoming, target.allOutgoing)
59
+ const reverse = reverseIntersection.size > 0
60
+ ? [
61
+ new ConnectionModel(
62
+ target,
63
+ source,
64
+ reverseIntersection
65
+ )
66
+ ] as const
67
+ : [] as const
68
+
69
+ return [...directed, ...reverse] as const
70
+ }
71
+
72
+ /**
73
+ * Resolve all connections between element and others
74
+ * By default, look for both directions.
75
+ */
76
+ export function findConnectionsBetween<M extends AnyAux>(
77
+ element: ElementModel<M>,
78
+ others: Iterable<ElementModel<NoInfer<M>>>,
79
+ direction: 'directed' | 'both' = 'both'
80
+ ): readonly ConnectionModel<M>[] {
81
+ if (element.allIncoming.size === 0 && element.allOutgoing.size === 0) {
82
+ return []
83
+ }
84
+
85
+ // We separate resolved connection,
86
+ // because we want return outgoing first
87
+ const outgoing = [] as ConnectionModel<M>[]
88
+ const incoming = [] as ConnectionModel<M>[]
89
+ for (const _other of others) {
90
+ if (element === _other) {
91
+ continue
92
+ }
93
+ for (const found of findConnection(element, _other, direction)) {
94
+ if (found.source === element) {
95
+ outgoing.push(found)
96
+ } else {
97
+ incoming.push(found)
98
+ }
99
+ }
100
+ }
101
+ return [
102
+ ...outgoing,
103
+ ...incoming
104
+ ]
105
+ }
106
+
107
+ /**
108
+ * Resolve all connections within a given set of elements
109
+ */
110
+ export function findConnectionsWithin<M extends AnyAux>(
111
+ elements: Iterable<ElementModel<M>>
112
+ ): readonly ConnectionModel<M>[] {
113
+ return [...elements].reduce((acc, el, index, array) => {
114
+ // skip for last element
115
+ if (index === array.length - 1) {
116
+ return acc
117
+ }
118
+ acc.push(
119
+ ...findConnectionsBetween(el, array.slice(index + 1), 'both')
120
+ )
121
+ return acc
122
+ }, [] as ConnectionModel<M>[])
123
+ }
@@ -1,3 +1,5 @@
1
- export { LikeC4DiagramModel } from './LikeC4DiagramModel'
1
+ export { LikeC4DeploymentModel } from './DeploymentModel'
2
2
  export { LikeC4Model } from './LikeC4Model'
3
- export { LikeC4ViewModel } from './LikeC4ViewModel'
3
+ export type * from './LikeC4Model'
4
+ export type { Aux as LikeC4ModelAux } from './types'
5
+ export { LikeC4ViewModel } from './view/LikeC4ViewModel'
@@ -1,23 +1,103 @@
1
- import type { Fqn as CoreFqn } from '../types/element'
2
- import type { RelationID as CoreRelationID } from '../types/relation'
3
- import type { EdgeId as CoreEdgeId, ViewID as CoreViewID } from '../types/view'
4
-
5
- import { isString } from 'remeda'
6
- import type { LiteralUnion } from 'type-fest'
7
-
8
- export type Fqn = LiteralUnion<CoreFqn, string>
9
- export type RelationID = LiteralUnion<CoreRelationID, string>
10
- export type ViewID = LiteralUnion<CoreViewID, string>
11
- export type EdgeId = LiteralUnion<CoreEdgeId, string>
1
+ import type { IsLiteral, LiteralUnion, Simplify } from 'type-fest'
2
+ import type {
3
+ AnyLikeC4Model,
4
+ ComputedView,
5
+ DiagramView,
6
+ EdgeId,
7
+ Fqn,
8
+ KeysOf,
9
+ LayoutedLikeC4Model,
10
+ NodeId,
11
+ ParsedLikeC4ModelDump,
12
+ RelationId,
13
+ ViewId
14
+ } from '../types'
15
+ import { isString } from '../utils/guards'
12
16
 
13
17
  export type IncomingFilter = 'all' | 'direct' | 'to-descendants'
14
18
  export type OutgoingFilter = 'all' | 'direct' | 'from-descendants'
15
19
 
16
- export type ElementOrFqn = Fqn | { id: Fqn }
17
-
18
20
  /**
19
21
  * Utility function to extract `id` from the given element.
20
22
  */
21
- export function getId(element: ElementOrFqn): Fqn {
22
- return isString(element) ? element : element.id
23
+ export function getId<Id extends string>(element: string | { id: Id }): Id {
24
+ return isString(element) ? element as Id : element.id
23
25
  }
26
+
27
+ export type IteratorLike<T> = IteratorObject<T, BuiltinIteratorReturn>
28
+
29
+ type WithId<Id> = { id: Id }
30
+
31
+ type WithViews<Id extends string, ViewType> = { views: Record<Id, ViewType> }
32
+
33
+ /**
34
+ * Auxilary type to keep track
35
+ */
36
+ export interface Aux<
37
+ Element extends string,
38
+ Deployment extends string,
39
+ Views extends string,
40
+ ViewType
41
+ > {
42
+ // Model: Model
43
+
44
+ // If Fqn is just a string, then we use generic Fqn to have better hints in the editor
45
+ Fqn: IsLiteral<Element> extends true ? Fqn<Element> : Fqn
46
+ Element: Element
47
+ ElementOrFqn: Element | WithId<this['Fqn']>
48
+
49
+ DeploymentFqn: IsLiteral<Deployment> extends true ? Fqn<Deployment> : Fqn
50
+ Deployment: Deployment
51
+ DeploymentOrFqn: Deployment | WithId<this['DeploymentFqn']>
52
+
53
+ ViewId: IsLiteral<Views> extends true ? ViewId<Views> : ViewId
54
+ ViewIdLiteral: Views
55
+ ViewType: ViewType
56
+
57
+ RelationId: RelationId
58
+ NodeId: NodeId
59
+ NodeIdLiteral: string
60
+ EdgeId: EdgeId
61
+ EdgeIdLiteral: string
62
+
63
+ NodeOrId: LiteralUnion<this['NodeIdLiteral'], string> | WithId<this['NodeId']>
64
+ EdgeOrId: LiteralUnion<this['EdgeIdLiteral'], string> | WithId<this['EdgeId']>
65
+
66
+ Model: Simplify<Omit<AnyLikeC4Model, 'views'> & WithViews<this['ViewId'], ViewType>>
67
+ }
68
+
69
+ export type AnyAux = Aux<
70
+ string,
71
+ string,
72
+ string,
73
+ any
74
+ >
75
+
76
+ export namespace Aux {
77
+ export type FromModel<M> = M extends AnyLikeC4Model ? Aux<
78
+ KeysOf<M['elements']>,
79
+ KeysOf<M['deployments']['elements']>,
80
+ KeysOf<M['views']>,
81
+ M extends LayoutedLikeC4Model ? DiagramView<ViewId<KeysOf<M['views']>>, string>
82
+ : ComputedView<ViewId<KeysOf<M['views']>>, string>
83
+ >
84
+ : never
85
+
86
+ export type FromDump<M> = M extends ParsedLikeC4ModelDump ? Aux<
87
+ KeysOf<M['elements']>,
88
+ KeysOf<M['deployments']['elements']>,
89
+ KeysOf<M['views']>,
90
+ DiagramView<ViewId<KeysOf<M['views']>>, string>
91
+ >
92
+ : never
93
+ }
94
+
95
+ // export type FromModel<M> = LikeC4Model<
96
+ // M extends AnyLikeC4Model<
97
+ // infer Fqn extends string,
98
+ // infer Deployment extends string,
99
+ // infer ViewId extends string,
100
+ // any
101
+ // > ? Aux<Fqn, Deployment, ViewId, M>
102
+ // : Aux<KeysOf<>, string, string, ComputedLikeC4Model | LayoutedLikeC4Model>
103
+ // >
@@ -0,0 +1,103 @@
1
+ import { isNonNullish } from 'remeda'
2
+ import type { LiteralUnion } from 'type-fest'
3
+ import {
4
+ type ComputedDynamicView,
5
+ type ComputedView,
6
+ type DiagramView,
7
+ extractStep,
8
+ isStepEdgeId,
9
+ type RelationId as C4RelationID,
10
+ type StepEdgeId,
11
+ type Tag as C4Tag
12
+ } from '../../types'
13
+ import type { DeploymentRelationModel } from '../DeploymentElementModel'
14
+ import type { LikeC4Model } from '../LikeC4Model'
15
+ import type { RelationshipModel } from '../RelationModel'
16
+ import type { AnyAux, IteratorLike } from '../types'
17
+ import type { LikeC4ViewModel } from './LikeC4ViewModel'
18
+ import type { NodeModel } from './NodeModel'
19
+
20
+ export type EdgesIterator<M extends AnyAux, V extends ComputedView | DiagramView> = IteratorLike<EdgeModel<M, V>>
21
+
22
+ export class EdgeModel<M extends AnyAux, V extends ComputedView | DiagramView = M['ViewType']> {
23
+ constructor(
24
+ public readonly view: LikeC4ViewModel<M, V>,
25
+ public readonly $edge: V['edges'][number],
26
+ public readonly source: NodeModel<M, V>,
27
+ public readonly target: NodeModel<M, V>
28
+ ) {
29
+ }
30
+
31
+ get id(): M['EdgeId'] {
32
+ return this.$edge.id
33
+ }
34
+
35
+ get parent(): NodeModel<M, V> | null {
36
+ return this.$edge.parent ? this.view.node(this.$edge.parent) : null
37
+ }
38
+
39
+ get label(): string | null {
40
+ return this.$edge.label
41
+ }
42
+
43
+ get description(): string | null {
44
+ return this.$edge.description ?? null
45
+ }
46
+
47
+ get technology(): string | null {
48
+ return this.$edge.technology ?? null
49
+ }
50
+
51
+ public hasParent(): this is EdgeModel.WithParent<M, V> {
52
+ return this.$edge.parent !== null
53
+ }
54
+
55
+ get tags(): ReadonlyArray<C4Tag> {
56
+ return this.$edge.tags ?? []
57
+ }
58
+
59
+ get stepNumber(): number | null {
60
+ return this.isStep() ? extractStep(this.id) : null
61
+ }
62
+
63
+ get navigateTo(): LikeC4ViewModel<M> | null {
64
+ return this.$edge.navigateTo ? this.view.$model.view(this.$edge.navigateTo) : null
65
+ }
66
+
67
+ public isStep(): this is EdgeModel.StepEdge<M, ComputedDynamicView> {
68
+ return isStepEdgeId(this.id)
69
+ }
70
+
71
+ public relationships(type: 'model'): IteratorLike<RelationshipModel<M>>
72
+ public relationships(type: 'deployment'): IteratorLike<DeploymentRelationModel<M>>
73
+ public relationships(type?: 'model' | 'deployment'): IteratorLike<LikeC4Model.AnyRelation<M>>
74
+ public *relationships(type: 'model' | 'deployment' | undefined): IteratorLike<LikeC4Model.AnyRelation<M>> {
75
+ for (const id of this.$edge.relations) {
76
+ // if type is provided, then we need to filter relationships
77
+ if (type) {
78
+ yield this.view.$model.relationship(id, type)
79
+ const rel = this.view.$model.findRelationship(id, type)
80
+ if (rel) {
81
+ yield rel
82
+ }
83
+ } else {
84
+ yield this.view.$model.relationship(id)
85
+ }
86
+ }
87
+ return
88
+ }
89
+
90
+ public includesRelation(rel: M['RelationId']): boolean {
91
+ return this.$edge.relations.includes(rel as C4RelationID)
92
+ }
93
+ }
94
+
95
+ namespace EdgeModel {
96
+ export interface StepEdge<M extends AnyAux, V extends ComputedView | DiagramView> extends EdgeModel<M, V> {
97
+ id: StepEdgeId
98
+ stepNumber: number
99
+ }
100
+ export interface WithParent<M extends AnyAux, V extends ComputedView | DiagramView> extends EdgeModel<M, V> {
101
+ parent: NodeModel<M, V>
102
+ }
103
+ }