@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,84 @@
1
+ import { isEmpty } from 'remeda'
2
+ import type { Link, Tag } from '../types/element'
3
+ import type { Relation } from '../types/relation'
4
+ import { commonAncestor } from '../utils/fqn'
5
+ import type { ElementModel } from './ElementModel'
6
+ import type { LikeC4Model } from './LikeC4Model'
7
+ import type { AnyAux, IteratorLike } from './types'
8
+ import type { LikeC4ViewModel, ViewsIterator } from './view/LikeC4ViewModel'
9
+
10
+ export type RelationshipsIterator<M extends AnyAux> = IteratorLike<RelationshipModel<M>>
11
+
12
+ export class RelationshipModel<M extends AnyAux> {
13
+ public readonly source: ElementModel<M>
14
+ public readonly target: ElementModel<M>
15
+
16
+ /**
17
+ * Common ancestor of the source and target elements.
18
+ * Represents the boundary of the Relation.
19
+ */
20
+ public readonly boundary: ElementModel<M> | null
21
+
22
+ constructor(
23
+ public readonly model: LikeC4Model<M>,
24
+ public readonly $relationship: Relation
25
+ ) {
26
+ this.source = model.element($relationship.source)
27
+ this.target = model.element($relationship.target)
28
+ const parent = commonAncestor(this.source.id, this.target.id)
29
+ this.boundary = parent ? this.model.element(parent) : null
30
+ }
31
+
32
+ get id(): M['RelationId'] {
33
+ return this.$relationship.id
34
+ }
35
+
36
+ get expression(): string {
37
+ return `${this.source.id} -> ${this.target.id}`
38
+ }
39
+
40
+ get title(): string | null {
41
+ if (isEmpty(this.$relationship.title)) {
42
+ return null
43
+ }
44
+ return this.$relationship.title
45
+ }
46
+
47
+ get technology(): string | null {
48
+ if (isEmpty(this.$relationship.technology)) {
49
+ return null
50
+ }
51
+ return this.$relationship.technology
52
+ }
53
+
54
+ get description(): string | null {
55
+ if (isEmpty(this.$relationship.description)) {
56
+ return null
57
+ }
58
+ return this.$relationship.description
59
+ }
60
+
61
+ get navigateTo(): LikeC4ViewModel<M> | null {
62
+ return this.$relationship.navigateTo ? this.model.view(this.$relationship.navigateTo) : null
63
+ }
64
+
65
+ get tags(): ReadonlyArray<Tag> {
66
+ return this.$relationship.tags ?? []
67
+ }
68
+
69
+ get links(): ReadonlyArray<Link> {
70
+ return this.$relationship.links ?? []
71
+ }
72
+
73
+ /**
74
+ * Iterate over all views that include this relationship.
75
+ */
76
+ public *views(): ViewsIterator<M> {
77
+ for (const view of this.model.views()) {
78
+ if (view.includesRelation(this.id)) {
79
+ yield view
80
+ }
81
+ }
82
+ return
83
+ }
84
+ }
@@ -1,343 +1,158 @@
1
- import { mapToObj } from 'remeda'
2
- import type { LiteralUnion, Tagged, UnwrapTagged } from 'type-fest'
3
- import {
4
- type ComputedLikeC4Model,
5
- type Element,
6
- type ElementKindSpecification,
7
- type NonEmptyArray,
8
- type Relation,
9
- type RelationshipArrowType,
10
- type RelationshipLineType,
11
- type Tag,
12
- type ThemeColor
13
- } from '../../types'
14
-
15
- /**
16
- ┌──────────────────────────────────────────────────┐
17
- │ cloud │
18
- │ ┌───────────────────────────────────────────┐ │
19
- │ │ frontend │ │
20
- ┏━━━━━━━━━━┓ │ │ ┏━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━━┓ │ │ ┏━━━━━━━━━━━┓
21
- ┃ ┃ │ │ ┃ ┃ ┃ ┃ │ │ ┃ ┃
22
- ┃ customer ┃──┼──┼──▶┃ dashboard ┃ ┃ adminpanel ┃◀───┼───┼───┃ support ┃
23
- ┃ ┃ │ │ ┃ ┃ ┃ ┃ │ │ ┃ ┃
24
- ┗━━━━━━━━━━┛ │ │ ┗━━━━━━┳━━━━━━┛ ┗━━━━━━━━┳━━━━━━━┛ │ │ ┗━━━━━━━━━━━┛
25
- │ └──────────┼───────────────────┼────────────┘ │
26
- │ ├───────────────────┘ │
27
- │ │ │
28
- │ ┌──────────┼────────────────────────────────┐ │
29
- │ │ ▼ backend │ │
30
- │ │ ┏━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━┓ │ │
31
- │ │ ┃ ┃ ┃ ┃ │ │
32
- │ ┃ graphlql ┃──────▶┃ storage ┃ │ │
33
- │ │ ┃ ┃ ┃ ┃ │ │
34
- │ ┗━━━━━━━━━━━━━┛ ┗━━━━━━┳━━━━━━┛ │ │
35
- │ └────────────────────────────────┼──────────┘ │
36
- └───────────────────────────────────┼──────────────┘
37
-
38
- ┌─────────┼─────────┐
39
- │ amazon │ │
40
- │ ▼ │
41
- │ ┏━━━━━━━━━━━━━━┓ │
42
- │ ┃ ┃ │
43
- │ ┃ s3 ┃ │
44
- │ ┃ ┃ │
45
- │ ┗━━━━━━━━━━━━━━┛ │
46
- └───────────────────┘
47
-
48
- specification {
49
- element actor
50
- element system
51
- element container
52
- element component
53
-
54
- tag old
55
- }
56
-
57
- model {
58
-
59
- actor customer
60
- actor support
61
-
62
- system cloud {
63
- container backend {
64
- component graphql
65
- component storage {
66
- #old
67
- }
68
-
69
- graphql -> storage
70
- }
71
-
72
- container frontend {
73
- component dashboard {
74
- -> graphql
1
+ import { Builder } from '../../builder/Builder'
2
+ import { computeViews } from '../../compute-view/compute-view'
3
+ import { LikeC4Model } from '../LikeC4Model'
4
+
5
+ const {
6
+ builder: b,
7
+ model: {
8
+ person,
9
+ system,
10
+ component,
11
+ webapp,
12
+ mobile,
13
+ ...$m
14
+ },
15
+ deployment: {
16
+ deployment,
17
+ env,
18
+ node,
19
+ zone,
20
+ instanceOf,
21
+ ...$d
22
+ },
23
+ views: {
24
+ view,
25
+ views,
26
+ viewOf,
27
+ deploymentView,
28
+ $rules,
29
+ $include,
30
+ $exclude,
31
+ $style
32
+ }
33
+ } = Builder.forSpecification({
34
+ elements: {
35
+ person: {
36
+ style: {
37
+ shape: 'person'
75
38
  }
76
- component adminPanel {
77
- #old
78
- -> graphql
39
+ },
40
+ system: {},
41
+ component: {},
42
+ webapp: {
43
+ style: {
44
+ shape: 'browser'
79
45
  }
80
- }
81
- }
82
-
83
- customer -> dashboard
84
- support -> adminPanel
85
-
86
- system amazon {
87
- component s3
88
-
89
- cloud.backend.storage -> s3
90
- }
91
-
92
- }
93
-
94
- */
95
- type TestTag = 'old' | 'next' | 'aws' | 'storage' | 'communication' | 'legacy'
96
-
97
- const el = <Id extends string, Kind extends string>({
98
- id,
99
- kind,
100
- title,
101
- style,
102
- tags,
103
- ...props
104
- }: Partial<Omit<Element, 'id' | 'kind' | 'tags'>> & {
105
- id: Id
106
- kind: Kind
107
- tags?: NonEmptyArray<TestTag>
108
- }) =>
109
- ({
110
- id: id as Tagged<Id, 'Fqn'>,
111
- kind: kind as Tagged<Kind, 'ElementKind'>,
112
- title: title ?? id,
113
- description: null,
114
- technology: null,
115
- tags: tags as NonEmptyArray<Tag> ?? null,
116
- links: null,
117
- style: {
118
- ...style
119
46
  },
120
- ...props
121
- }) satisfies Element
122
-
123
- const fakeElementsArr = [
124
- el({
125
- id: 'customer',
126
- kind: 'actor',
127
- title: 'customer',
128
- shape: 'person'
129
- }),
130
- el({
131
- id: 'support',
132
- kind: 'actor',
133
- title: 'support',
134
- shape: 'person'
135
- }),
136
- el({
137
- id: 'cloud',
138
- kind: 'system',
139
- title: 'cloud',
140
- tags: ['next', 'old']
141
- }),
142
- el({
143
- id: 'cloud.backend',
144
- kind: 'container',
145
- title: 'backend'
146
- }),
147
- el({
148
- id: 'cloud.frontend',
149
- kind: 'container',
150
- title: 'frontend',
151
- shape: 'browser'
152
- }),
153
- el({
154
- id: 'cloud.backend.graphql',
155
- kind: 'component',
156
- title: 'graphql'
157
- }),
158
- el({
159
- id: 'email',
160
- kind: 'system',
161
- title: 'email'
162
- }),
163
- el({
164
- id: 'cloud.backend.storage',
165
- kind: 'component',
166
- title: 'storage',
167
- tags: ['storage', 'old']
168
- }),
169
- el({
170
- id: 'cloud.frontend.adminPanel',
171
- kind: 'component',
172
- title: 'adminPanel',
173
- tags: ['old']
174
- }),
175
- el({
176
- id: 'cloud.frontend.dashboard',
177
- kind: 'component',
178
- title: 'dashboard',
179
- tags: ['next']
180
- }),
181
- el({
182
- id: 'amazon',
183
- kind: 'system',
184
- title: 'amazon',
185
- tags: ['aws']
186
- }),
187
- el({
188
- id: 'amazon.s3',
189
- kind: 'component',
190
- title: 's3',
191
- shape: 'storage',
192
- tags: ['aws', 'storage']
193
- })
194
- ] as const
195
-
196
- export type FakeElementIds = UnwrapTagged<(typeof fakeElementsArr)[number]['id']>
197
- export type FakeElementKinds = UnwrapTagged<(typeof fakeElementsArr)[number]['kind']>
198
-
199
- export const fakeElements = mapToObj(fakeElementsArr, e => [e.id as FakeElementIds, e])
200
-
201
- const rel = <Source extends FakeElementIds, Target extends FakeElementIds, Kind extends string>({
202
- source,
203
- target,
204
- title,
205
- kind,
206
- tags,
207
- ...props
208
- }: {
209
- source: LiteralUnion<Source, string>
210
- target: LiteralUnion<Target, string>
211
- title?: string
212
- kind?: LiteralUnion<Kind, string>
213
- color?: ThemeColor
214
- line?: RelationshipLineType
215
- head?: RelationshipArrowType
216
- tail?: RelationshipArrowType
217
- tags?: NonEmptyArray<TestTag>
218
- }) =>
219
- ({
220
- id: `${source}:${target}` as Tagged<`${Source}:${Target}`, 'RelationID'>,
221
- title: title ?? '',
222
- source: source as Tagged<Source, 'Fqn'>,
223
- target: target as Tagged<Target, 'Fqn'>,
224
- ...(kind ? { kind: kind as Tagged<Kind, 'RelationshipKind'> } : {}),
225
- tags: tags as NonEmptyArray<Tag> ?? null,
226
- ...props
227
- }) satisfies Relation
228
-
229
- const fakeRelationsArr = [
230
- rel({
231
- source: 'customer',
232
- target: 'cloud.frontend.dashboard',
233
- title: 'opens in browser'
234
- }),
235
- rel({
236
- source: 'support',
237
- target: 'cloud.frontend.adminPanel',
238
- title: 'manages'
239
- }),
240
- rel({
241
- source: 'cloud.backend.storage',
242
- target: 'amazon.s3',
243
- title: 'uploads',
244
- tags: ['aws', 'storage', 'legacy']
245
- }),
246
- rel({
247
- source: 'customer',
248
- target: 'cloud',
249
- title: 'uses'
250
- }),
251
- rel({
252
- source: 'customer',
253
- target: 'cloud.frontend',
254
- title: 'uses frontend'
255
- }),
256
- rel({
257
- source: 'cloud.backend.graphql',
258
- target: 'cloud.backend.storage',
259
- title: 'stores',
260
- tags: ['old', 'storage']
261
- }),
262
- rel({
263
- source: 'cloud.frontend',
264
- target: 'cloud.backend',
265
- title: 'requests'
266
- }),
267
- rel({
268
- source: 'cloud.frontend.dashboard',
269
- target: 'cloud.backend.graphql',
270
- kind: 'graphlql',
271
- title: 'requests',
272
- line: 'solid',
273
- tags: ['next']
274
- }),
275
- rel({
276
- source: 'cloud.frontend.adminPanel',
277
- target: 'cloud.backend.graphql',
278
- kind: 'graphlql',
279
- title: 'fetches',
280
- line: 'dashed',
281
- tail: 'odiamond',
282
- tags: ['old']
283
- }),
284
- rel({
285
- source: 'cloud',
286
- target: 'amazon',
287
- title: 'uses',
288
- head: 'diamond',
289
- tail: 'odiamond',
290
- tags: ['aws']
291
- }),
292
- rel({
293
- source: 'cloud.backend',
294
- target: 'email',
295
- title: 'schedule',
296
- tags: ['communication']
297
- }),
298
- rel({
299
- source: 'cloud',
300
- target: 'email',
301
- title: 'uses',
302
- tags: ['communication']
303
- }),
304
- rel({
305
- source: 'email',
306
- target: 'cloud',
307
- title: 'notifies',
308
- tags: ['communication']
309
- })
310
- ] as const
311
-
312
- export type FakeRelationIds = UnwrapTagged<(typeof fakeRelationsArr)[number]['id']>
313
-
314
- export const fakeRelations = mapToObj(fakeRelationsArr, r => [r.id as FakeRelationIds, r])
315
-
316
- export const fakeComputedModel = {
317
- specification: {
318
- tags: [],
319
- relationships: {},
320
- elements: {
321
- actor: {
322
- style: {}
323
- },
324
- system: {
325
- style: {}
326
- },
327
- container: {
328
- style: {}
329
- },
330
- component: {
331
- style: {}
47
+ mobile: {
48
+ style: {
49
+ shape: 'mobile'
332
50
  }
333
- } satisfies Record<FakeElementKinds, ElementKindSpecification>
334
- },
335
- elements: fakeElements,
336
- relations: fakeRelations,
337
- globals: {
338
- predicates: {},
339
- dynamicPredicates: {},
340
- styles: {}
51
+ }
341
52
  },
342
- views: {}
343
- } satisfies ComputedLikeC4Model
53
+ deployments: {
54
+ env: {},
55
+ zone: {},
56
+ node: {}
57
+ }
58
+ })
59
+
60
+ const local = b
61
+ .with(
62
+ $m.model(
63
+ person('customer'),
64
+ system('cloud').with(
65
+ component('frontend').with(
66
+ webapp('dashboard'),
67
+ mobile('mobile')
68
+ ),
69
+ component('auth'),
70
+ component('backend').with(
71
+ component('api'),
72
+ component('graphql')
73
+ ),
74
+ component('media', {
75
+ shape: 'storage'
76
+ })
77
+ ),
78
+ system('aws').with(
79
+ component('rds', {
80
+ shape: 'storage'
81
+ }),
82
+ component('s3', {
83
+ shape: 'storage'
84
+ })
85
+ ),
86
+ system('email')
87
+ ),
88
+ $m.model(
89
+ $m.rel('customer', 'cloud', 'uses services'),
90
+ $m.rel('customer', 'cloud.frontend.mobile', 'opens mobile app'),
91
+ $m.rel('customer', 'cloud.frontend.dashboard', 'opens in browser'),
92
+ $m.rel('cloud.frontend.dashboard', 'cloud.auth', 'authenticates'),
93
+ $m.rel('cloud.frontend.dashboard', 'cloud.backend.api', 'fetches data'),
94
+ $m.rel('cloud.frontend.dashboard', 'cloud.media', 'fetches media'),
95
+ $m.rel('cloud.frontend.mobile', 'cloud.auth', 'authenticates'),
96
+ $m.rel('cloud.frontend.mobile', 'cloud.backend.api', 'fetches data'),
97
+ $m.rel('cloud.frontend.mobile', 'cloud.media', 'fetches media'),
98
+ $m.rel('cloud.frontend', 'cloud.backend')
99
+ ),
100
+ $m.model(
101
+ $m.rel('cloud.backend.api', 'cloud.auth', 'authorizes'),
102
+ $m.rel('cloud.backend.api', 'cloud.media', 'uploads media'),
103
+ $m.rel('cloud.backend.api', 'aws.rds', 'reads/writes'),
104
+ $m.rel('cloud.backend.api', 'email', 'sends emails'),
105
+ $m.rel('cloud.media', 'aws.s3', 'uploads'),
106
+ $m.rel('email', 'customer', 'sends emails')
107
+ ),
108
+ deployment(
109
+ node('customer').with(
110
+ instanceOf('instance', 'customer')
111
+ ),
112
+ env('prod').with(
113
+ zone('eu').with(
114
+ zone('zone1').with(
115
+ instanceOf('ui', 'cloud.frontend.dashboard'),
116
+ instanceOf('api', 'cloud.backend.api')
117
+ ),
118
+ zone('zone2').with(
119
+ instanceOf('ui', 'cloud.frontend.dashboard'),
120
+ instanceOf('api', 'cloud.backend.api')
121
+ ),
122
+ instanceOf('media', 'cloud.media'),
123
+ instanceOf('db', 'aws.rds')
124
+ ),
125
+ zone('us').with(
126
+ instanceOf('db', 'aws.rds')
127
+ )
128
+ ),
129
+ $d.rel('prod.eu.db', 'prod.us.db', 'replicates')
130
+ ),
131
+ views(
132
+ view('index', $include('*')),
133
+ viewOf(
134
+ 'cloud',
135
+ 'cloud',
136
+ $rules(
137
+ $include('*'),
138
+ $include('cloud.frontend.dashboard')
139
+ )
140
+ ),
141
+ deploymentView(
142
+ 'prod',
143
+ $rules(
144
+ $include('customer.instance'),
145
+ $include('prod.eu.zone1.ui')
146
+ )
147
+ )
148
+ )
149
+ )
150
+
151
+ export const builder = local.clone()
152
+
153
+ export const parsed = local.build()
154
+ export type TestFqn = typeof local.Types.Fqn
155
+
156
+ export const computed = computeViews(parsed)
157
+
158
+ export const model = LikeC4Model.create(computed)
@@ -0,0 +1,94 @@
1
+ import { invariant } from '../../errors'
2
+ import { difference, equals, union } from '../../utils/set'
3
+ import { stringHash } from '../../utils/string-hash'
4
+ import type { ElementModel } from '../ElementModel'
5
+ import type { RelationshipModel } from '../RelationModel'
6
+ import type { AnyAux } from '../types'
7
+
8
+ export interface Connection<Elem = any, Id = any> {
9
+ readonly id: Id
10
+ readonly source: Elem
11
+ readonly target: Elem
12
+
13
+ /**
14
+ * Common ancestor of the source and target elements.
15
+ * Represents the boundary of the connection.
16
+ */
17
+ readonly boundary: Elem | null
18
+
19
+ /**
20
+ * Human readable expression of the connection
21
+ * Mostly used for testing and debugging
22
+ */
23
+ readonly expression: string
24
+
25
+ mergeWith(this: Connection<any, any>, other: typeof this): typeof this
26
+
27
+ nonEmpty(): boolean
28
+
29
+ difference(this: Connection<Elem, Id>, other: Connection<Elem, Id>): typeof this
30
+ }
31
+
32
+ /**
33
+ * Connection refers to any relationships between two elements,
34
+ * both direct and implicit ones (between their nested elements).
35
+ *
36
+ * Merges relationships together to an single edge on the diagram.
37
+ */
38
+ export class ConnectionModel<M extends AnyAux = AnyAux> implements Connection<ElementModel<M>, M['EdgeId']> {
39
+ public readonly id: M['EdgeId']
40
+
41
+ /**
42
+ * Common ancestor of the source and target elements.
43
+ * Represents the boundary of the connection.
44
+ */
45
+ public readonly boundary: ElementModel<M> | null
46
+
47
+ constructor(
48
+ public readonly source: ElementModel<M>,
49
+ public readonly target: ElementModel<M>,
50
+ public readonly relations: ReadonlySet<RelationshipModel<M>>
51
+ ) {
52
+ this.id = stringHash(`model:${source.id}:${target.id}`) as M['EdgeId']
53
+ this.boundary = source.commonAncestor(target)
54
+ }
55
+
56
+ /**
57
+ * Human readable expression of the connection
58
+ * Mostly used for testing and debugging
59
+ */
60
+ get expression(): string {
61
+ return `${this.source.id} -> ${this.target.id}`
62
+ }
63
+
64
+ nonEmpty(): boolean {
65
+ return this.relations.size > 0
66
+ }
67
+
68
+ mergeWith(other: ConnectionModel<M>): ConnectionModel<M> {
69
+ invariant(this.source.id === other.source.id, 'Cannot merge connections with different sources')
70
+ invariant(this.target.id === other.target.id, 'Cannot merge connections with different targets')
71
+ return new ConnectionModel(
72
+ this.source,
73
+ this.target,
74
+ union(this.relations, other.relations)
75
+ )
76
+ }
77
+
78
+ difference(other: ConnectionModel<M>): ConnectionModel<M> {
79
+ invariant(this.source.id === other.source.id, 'Cannot difference connection with different sources')
80
+ invariant(this.target.id === other.target.id, 'Cannot difference connection with different targets')
81
+ return new ConnectionModel(
82
+ this.source,
83
+ this.target,
84
+ difference(this.relations, other.relations)
85
+ )
86
+ }
87
+
88
+ equals(other: ConnectionModel<M>): boolean {
89
+ return this.id === other.id
90
+ && this.source.id === other.source.id
91
+ && this.target.id === other.target.id
92
+ && equals(this.relations, other.relations)
93
+ }
94
+ }