@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,675 @@
1
+ import { indexBy, isString, map, prop } from 'remeda'
2
+ import { LikeC4Model } from '../../../model'
3
+ import {
4
+ type BorderStyle,
5
+ type Color,
6
+ type ComputedView,
7
+ type CustomElementExpr as C4CustomElementExpr,
8
+ type CustomRelationExpr as C4CustomRelationExpr,
9
+ type Element,
10
+ type ElementExpression as C4ElementExpression,
11
+ type ElementKind,
12
+ type ElementShape,
13
+ type ElementWhereExpr,
14
+ type Expression as C4Expression,
15
+ type Fqn,
16
+ type GlobalPredicateId,
17
+ type GlobalStyleID,
18
+ type IconUrl,
19
+ type IncomingExpr as C4IncomingExpr,
20
+ type InOutExpr as C4InOutExpr,
21
+ isElementRef,
22
+ isElementWhere,
23
+ isRelationExpression,
24
+ isRelationWhere,
25
+ type ModelRelation,
26
+ type NonEmptyArray,
27
+ type OutgoingExpr as C4OutgoingExpr,
28
+ type RelationId,
29
+ type RelationshipArrowType,
30
+ type RelationshipLineType,
31
+ type RelationWhereExpr,
32
+ type Tag,
33
+ type ViewId,
34
+ type ViewRule,
35
+ type ViewRuleGlobalPredicateRef,
36
+ type ViewRuleGlobalStyle,
37
+ type ViewRuleGroup,
38
+ type ViewRulePredicate,
39
+ type ViewRuleStyle,
40
+ type WhereOperator,
41
+ } from '../../../types'
42
+ import { type DirectRelationExpr as C4RelationExpr } from '../../../types/expression'
43
+ import { withReadableEdges } from '../../utils/with-readable-edges'
44
+ import { computeElementView } from '../compute'
45
+
46
+ /**
47
+ ┌──────────────────────────────────────────────────┐
48
+ │ cloud │
49
+ │ ┌───────────────────────────────────────────┐ │
50
+ │ │ frontend │ │
51
+ ┏━━━━━━━━━━┓ │ │ ┏━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━━┓ │ │ ┏━━━━━━━━━━━┓
52
+ ┃ ┃ │ │ ┃ ┃ ┃ ┃ │ │ ┃ ┃
53
+ ┃ customer ┃──┼──┼──▶┃ dashboard ┃ ┃ adminpanel ┃◀───┼───┼───┃ support ┃
54
+ ┃ ┃ │ │ ┃ ┃ ┃ ┃ │ │ ┃ ┃
55
+ ┗━━━━━━━━━━┛ │ │ ┗━━━━━━┳━━━━━━┛ ┗━━━━━━━━┳━━━━━━━┛ │ │ ┗━━━━━━━━━━━┛
56
+ │ └──────────┼───────────────────┼────────────┘ │
57
+ │ ├───────────────────┘ │
58
+ │ │ │
59
+ │ ┌──────────┼────────────────────────────────┐ │
60
+ │ │ ▼ backend │ │
61
+ │ │ ┏━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━┓ │ │
62
+ │ │ ┃ ┃ ┃ ┃ │ │
63
+ │ │ ┃ graphlql ┃──────▶┃ storage ┃ │ │
64
+ │ │ ┃ ┃ ┃ ┃ │ │
65
+ │ │ ┗━━━━━━━━━━━━━┛ ┗━━━━━━┳━━━━━━┛ │ │
66
+ │ └────────────────────────────────┼──────────┘ │
67
+ └───────────────────────────────────┼──────────────┘
68
+
69
+ ┌─────────┼─────────┐
70
+ │ amazon │ │
71
+ │ ▼ │
72
+ │ ┏━━━━━━━━━━━━━━┓ │
73
+ │ ┃ ┃ │
74
+ │ ┃ s3 ┃ │
75
+ │ ┃ ┃ │
76
+ │ ┗━━━━━━━━━━━━━━┛ │
77
+ └───────────────────┘
78
+
79
+ specification {
80
+ element actor
81
+ element system
82
+ element container
83
+ element component
84
+
85
+ tag old
86
+ }
87
+
88
+ model {
89
+
90
+ actor customer
91
+ actor support
92
+
93
+ system cloud {
94
+ container backend {
95
+ component graphql
96
+ component storage {
97
+ #old
98
+ }
99
+
100
+ graphql -> storage
101
+ }
102
+
103
+ container frontend {
104
+ component dashboard {
105
+ -> graphql
106
+ }
107
+ component adminPanel {
108
+ #old
109
+ -> graphql
110
+ }
111
+ }
112
+ }
113
+
114
+ customer -> dashboard
115
+ support -> adminPanel
116
+
117
+ system amazon {
118
+ component s3
119
+
120
+ cloud.backend.storage -> s3
121
+ }
122
+
123
+ }
124
+
125
+ */
126
+ type TestTag = 'old' | 'next' | 'aws' | 'storage' | 'communication' | 'legacy'
127
+
128
+ const el = ({
129
+ id,
130
+ kind,
131
+ title,
132
+ style,
133
+ tags,
134
+ ...props
135
+ }: Partial<Omit<Element, 'id' | 'kind' | 'tags'>> & {
136
+ id: string
137
+ kind: string
138
+ tags?: NonEmptyArray<TestTag>
139
+ }): Element => ({
140
+ id: id as Fqn,
141
+ kind: kind as ElementKind,
142
+ title: title ?? id,
143
+ description: null,
144
+ technology: null,
145
+ tags: tags as NonEmptyArray<Tag> ?? null,
146
+ links: null,
147
+ style: {
148
+ ...style,
149
+ },
150
+ ...props,
151
+ })
152
+
153
+ export const fakeElements = {
154
+ 'customer': el({
155
+ id: 'customer',
156
+ kind: 'actor',
157
+ title: 'customer',
158
+ shape: 'person',
159
+ }),
160
+ 'support': el({
161
+ id: 'support',
162
+ kind: 'actor',
163
+ title: 'support',
164
+ shape: 'person',
165
+ }),
166
+ 'cloud': el({
167
+ id: 'cloud',
168
+ kind: 'system',
169
+ title: 'cloud',
170
+ icon: 'none',
171
+ tags: ['next', 'old'],
172
+ }),
173
+ 'cloud.backend': el({
174
+ id: 'cloud.backend',
175
+ kind: 'container',
176
+ title: 'backend',
177
+ }),
178
+ 'cloud.frontend': el({
179
+ id: 'cloud.frontend',
180
+ kind: 'container',
181
+ title: 'frontend',
182
+ shape: 'browser',
183
+ }),
184
+ 'cloud.backend.graphql': el({
185
+ id: 'cloud.backend.graphql',
186
+ kind: 'component',
187
+ icon: 'tech:graphql' as IconUrl,
188
+ title: 'graphql',
189
+ }),
190
+ 'email': el({
191
+ id: 'email',
192
+ kind: 'system',
193
+ title: 'email',
194
+ }),
195
+ 'cloud.backend.storage': el({
196
+ id: 'cloud.backend.storage',
197
+ kind: 'component',
198
+ title: 'storage',
199
+ tags: ['storage', 'old'],
200
+ }),
201
+ 'cloud.frontend.dashboard': el({
202
+ id: 'cloud.frontend.dashboard',
203
+ kind: 'component',
204
+ title: 'dashboard',
205
+ icon: 'tech:react' as IconUrl,
206
+ tags: ['next'],
207
+ }),
208
+ 'cloud.frontend.supportPanel': el({
209
+ id: 'cloud.frontend.supportPanel',
210
+ kind: 'component',
211
+ title: 'adminPanel',
212
+ tags: ['old'],
213
+ }),
214
+ 'amazon': el({
215
+ id: 'amazon',
216
+ kind: 'system',
217
+ title: 'amazon',
218
+ icon: 'tech:aws' as IconUrl,
219
+ tags: ['aws'],
220
+ }),
221
+ 'amazon.s3': el({
222
+ id: 'amazon.s3',
223
+ kind: 'component',
224
+ title: 's3',
225
+ shape: 'storage',
226
+ icon: 'aws:s3' as IconUrl,
227
+ tags: ['aws', 'storage'],
228
+ }),
229
+ } satisfies Record<string, Element>
230
+
231
+ export type FakeElementIds = keyof typeof fakeElements
232
+
233
+ const rel = <Source extends FakeElementIds, Target extends FakeElementIds>({
234
+ source,
235
+ target,
236
+ title,
237
+ ...props
238
+ }: {
239
+ source: Source
240
+ target: Target
241
+ title?: string
242
+ kind?: string
243
+ color?: Color
244
+ line?: RelationshipLineType
245
+ head?: RelationshipArrowType
246
+ tail?: RelationshipArrowType
247
+ tags?: NonEmptyArray<TestTag>
248
+ }) =>
249
+ ({
250
+ id: `${source}:${target}` as RelationId,
251
+ title: title ?? '',
252
+ source: source as Fqn,
253
+ target: target as Fqn,
254
+ ...(props as any),
255
+ }) as Omit<ModelRelation, 'id'> & { id: `${Source}:${Target}` }
256
+
257
+ export const fakeRelations = [
258
+ rel({
259
+ source: 'customer',
260
+ target: 'cloud.frontend.dashboard',
261
+ title: 'opens in browser',
262
+ }),
263
+ rel({
264
+ source: 'support',
265
+ target: 'cloud.frontend.supportPanel',
266
+ title: 'manages',
267
+ }),
268
+ rel({
269
+ source: 'cloud.backend.storage',
270
+ target: 'amazon.s3',
271
+ title: 'uploads',
272
+ tags: ['aws', 'storage', 'legacy'],
273
+ }),
274
+ rel({
275
+ source: 'customer',
276
+ target: 'cloud',
277
+ title: 'uses',
278
+ }),
279
+ rel({
280
+ source: 'cloud.backend.graphql',
281
+ target: 'cloud.backend.storage',
282
+ title: 'stores',
283
+ tags: ['old', 'storage'],
284
+ }),
285
+ rel({
286
+ source: 'cloud.frontend',
287
+ target: 'cloud.backend',
288
+ title: 'requests',
289
+ }),
290
+ rel({
291
+ source: 'cloud.frontend.dashboard',
292
+ target: 'cloud.backend.graphql',
293
+ kind: 'graphlql',
294
+ title: 'requests',
295
+ line: 'solid',
296
+ tags: ['next'],
297
+ }),
298
+ rel({
299
+ source: 'cloud.frontend.supportPanel',
300
+ target: 'cloud.backend.graphql',
301
+ kind: 'graphlql',
302
+ title: 'fetches',
303
+ line: 'dashed',
304
+ tail: 'odiamond',
305
+ tags: ['old'],
306
+ }),
307
+ rel({
308
+ source: 'cloud',
309
+ target: 'amazon',
310
+ title: 'uses',
311
+ head: 'diamond',
312
+ tail: 'odiamond',
313
+ tags: ['aws'],
314
+ }),
315
+ rel({
316
+ source: 'cloud.backend',
317
+ target: 'email',
318
+ title: 'schedule',
319
+ tags: ['communication'],
320
+ }),
321
+ rel({
322
+ source: 'cloud',
323
+ target: 'email',
324
+ title: 'uses',
325
+ tags: ['communication'],
326
+ }),
327
+ rel({
328
+ source: 'email',
329
+ target: 'cloud',
330
+ title: 'notifies',
331
+ tags: ['communication'],
332
+ }),
333
+ ]
334
+
335
+ export const globalStyles = {
336
+ 'mute_old': [{
337
+ targets: [$expr({
338
+ elementTag: 'old' as Tag,
339
+ isEqual: true,
340
+ })],
341
+ style: {
342
+ color: 'muted',
343
+ },
344
+ }],
345
+ 'red_next': [{
346
+ targets: [$expr({
347
+ elementTag: 'next' as Tag,
348
+ isEqual: true,
349
+ })],
350
+ style: {
351
+ color: 'red',
352
+ },
353
+ }],
354
+ } as const
355
+
356
+ export type FakeRelationIds = (typeof fakeRelations)[number]['id']
357
+ const fakeParsedModel = {
358
+ elements: fakeElements,
359
+ relations: indexBy(fakeRelations, r => r.id),
360
+ deployments: {
361
+ elements: {},
362
+ relations: {},
363
+ },
364
+ views: {},
365
+ globals: {
366
+ predicates: {
367
+ 'remove_tag_old': [
368
+ $exclude('*', {
369
+ tag: { eq: 'old' },
370
+ }),
371
+ ],
372
+ 'remove_not_tag_old': [
373
+ $exclude('*', {
374
+ tag: { neq: 'old' },
375
+ }),
376
+ ],
377
+ 'include_next': [
378
+ $include('* -> *', {
379
+ where: {
380
+ and: [
381
+ {
382
+ or: [
383
+ { tag: { eq: 'communication' } },
384
+ { tag: { eq: 'next' } },
385
+ { tag: { eq: 'old' } },
386
+ ],
387
+ },
388
+ { tag: { neq: 'storage' } },
389
+ ],
390
+ },
391
+ }),
392
+ ],
393
+ } satisfies Record<string, NonEmptyArray<ViewRulePredicate>>,
394
+ dynamicPredicates: {},
395
+ styles: globalStyles,
396
+ },
397
+ } as const
398
+ export const fakeModel = LikeC4Model.fromDump(fakeParsedModel)
399
+
400
+ const emptyView = {
401
+ __: 'element' as const,
402
+ id: 'index' as ViewId,
403
+ title: null,
404
+ description: null,
405
+ tags: null,
406
+ links: null,
407
+ customColorDefinitions: {},
408
+ rules: [],
409
+ }
410
+
411
+ export const includeWildcard = {
412
+ include: [
413
+ {
414
+ wildcard: true,
415
+ },
416
+ ],
417
+ } satisfies ViewRule
418
+
419
+ export type ElementRefExpr =
420
+ | '*'
421
+ | FakeElementIds
422
+ | `${FakeElementIds}.*`
423
+ | `${FakeElementIds}.**`
424
+ | `${FakeElementIds}._`
425
+
426
+ type InOutExpr = `-> ${ElementRefExpr} ->`
427
+ type IncomingExpr = `-> ${ElementRefExpr}`
428
+ type OutgoingExpr = `${ElementRefExpr} ->`
429
+ type RelationKeyword = '->' | '<->'
430
+ type RelationExpr = `${ElementRefExpr} ${RelationKeyword} ${ElementRefExpr}`
431
+
432
+ export type Expression =
433
+ | ElementRefExpr
434
+ | InOutExpr
435
+ | IncomingExpr
436
+ | OutgoingExpr
437
+ | RelationExpr
438
+
439
+ export function $custom(
440
+ expr: ElementRefExpr,
441
+ props: {
442
+ title?: string
443
+ description?: string
444
+ technology?: string
445
+ shape?: ElementShape
446
+ color?: Color
447
+ border?: BorderStyle
448
+ icon?: string
449
+ opacity?: number
450
+ navigateTo?: string
451
+ },
452
+ ): C4CustomElementExpr {
453
+ return {
454
+ custom: {
455
+ expr: $expr(expr) as any,
456
+ ...props as any,
457
+ },
458
+ }
459
+ }
460
+
461
+ export function $customRelation(
462
+ relation: RelationExpr,
463
+ props: Omit<C4CustomRelationExpr['customRelation'], 'relation'>,
464
+ ): C4CustomRelationExpr {
465
+ return {
466
+ customRelation: {
467
+ relation: $expr(relation) as any,
468
+ ...props,
469
+ },
470
+ }
471
+ }
472
+
473
+ export function $where(
474
+ expr: Expression | C4Expression,
475
+ operator: WhereOperator<TestTag, string>,
476
+ ): ElementWhereExpr | RelationWhereExpr {
477
+ return {
478
+ where: {
479
+ expr: $expr(expr) as any,
480
+ condition: operator,
481
+ },
482
+ }
483
+ }
484
+
485
+ export function $inout(
486
+ expr: InOutExpr | C4ElementExpression,
487
+ ): C4InOutExpr {
488
+ const innerExpression = !isString(expr)
489
+ ? expr as C4Expression
490
+ : $expr(expr.replace(/->/g, '').trim() as ElementRefExpr) as any
491
+
492
+ return { inout: innerExpression }
493
+ }
494
+
495
+ export function $incoming(
496
+ expr: IncomingExpr | C4ElementExpression,
497
+ ): C4IncomingExpr {
498
+ const innerExpression = !isString(expr)
499
+ ? expr as C4Expression
500
+ : $expr(expr.replace('-> ', '') as ElementRefExpr) as any
501
+
502
+ return { incoming: innerExpression }
503
+ }
504
+
505
+ export function $outgoing(
506
+ expr: OutgoingExpr | C4ElementExpression,
507
+ ): C4OutgoingExpr {
508
+ const innerExpression = !isString(expr)
509
+ ? expr as C4Expression
510
+ : $expr(expr.replace(' ->', '') as ElementRefExpr) as any
511
+
512
+ return { outgoing: innerExpression }
513
+ }
514
+
515
+ export function $relation(
516
+ expr: RelationExpr,
517
+ ): C4RelationExpr {
518
+ const [source, target] = expr.split(/ -> | <-> /)
519
+ const isBidirectional = expr.includes(' <-> ')
520
+
521
+ return {
522
+ source: $expr(source as ElementRefExpr) as any,
523
+ target: $expr(target as ElementRefExpr) as any,
524
+ ...(isBidirectional && { isBidirectional }),
525
+ }
526
+ }
527
+
528
+ export function $expr(expr: Expression | C4Expression): C4Expression {
529
+ if (!isString(expr)) {
530
+ return expr as C4Expression
531
+ }
532
+ if (expr === '*') {
533
+ return { wildcard: true }
534
+ }
535
+ if (expr.startsWith('->')) {
536
+ return expr.endsWith('->') ? $inout(expr as InOutExpr) : $incoming(expr as IncomingExpr)
537
+ }
538
+ if (expr.endsWith(' ->')) {
539
+ return $outgoing(expr as OutgoingExpr)
540
+ }
541
+ if (expr.includes(' -> ') || expr.includes(' <-> ')) {
542
+ return $relation(expr as RelationExpr)
543
+ }
544
+ if (expr.endsWith('._')) {
545
+ return {
546
+ expanded: expr.replace('._', '') as Fqn,
547
+ }
548
+ }
549
+ if (expr.endsWith('.*')) {
550
+ return {
551
+ element: expr.replace('.*', '') as Fqn,
552
+ isChildren: true,
553
+ }
554
+ }
555
+ if (expr.endsWith('.**')) {
556
+ return {
557
+ element: expr.replace('.**', '') as Fqn,
558
+ isDescendants: true,
559
+ }
560
+ }
561
+ return {
562
+ element: expr as Fqn,
563
+ }
564
+ }
565
+
566
+ type CustomProps = {
567
+ where?: WhereOperator<TestTag, string>
568
+ with?: {
569
+ title?: string
570
+ description?: string
571
+ technology?: string
572
+ shape?: ElementShape
573
+ color?: Color
574
+ border?: BorderStyle
575
+ icon?: string
576
+ opacity?: number
577
+ navigateTo?: string
578
+ } & Omit<C4CustomRelationExpr['customRelation'], 'relation' | 'navigateTo'>
579
+ }
580
+ export function $include(expr: Expression | C4Expression, props?: CustomProps): ViewRulePredicate {
581
+ let _expr = props?.where ? $where(expr, props.where) : $expr(expr)
582
+ _expr = props?.with ? $with(_expr, props.with) : _expr
583
+ return {
584
+ include: [_expr],
585
+ }
586
+ }
587
+ export function $with(expr: C4Expression, props?: CustomProps['with']): C4CustomRelationExpr | C4CustomElementExpr {
588
+ if (isRelationExpression(expr) || isRelationWhere(expr)) {
589
+ return {
590
+ customRelation: {
591
+ relation: expr,
592
+ ...props as any,
593
+ },
594
+ }
595
+ } else if (isElementRef(expr) || isElementWhere(expr)) {
596
+ return {
597
+ custom: {
598
+ expr: expr,
599
+ ...props as any,
600
+ },
601
+ }
602
+ }
603
+
604
+ throw 'Unsupported type of internal expression'
605
+ }
606
+ export function $exclude(expr: Expression | C4Expression, where?: WhereOperator<TestTag, string>): ViewRulePredicate {
607
+ let _expr = where ? $where(expr, where) : $expr(expr)
608
+ return {
609
+ exclude: [_expr],
610
+ }
611
+ }
612
+ export function $group(groupRules: ViewRuleGroup['groupRules']): ViewRuleGroup {
613
+ return {
614
+ title: null,
615
+ groupRules,
616
+ }
617
+ }
618
+
619
+ export function $style(element: ElementRefExpr, style: ViewRuleStyle['style']): ViewRuleStyle {
620
+ return {
621
+ targets: [$expr(element) as C4ElementExpression],
622
+ style: Object.assign({}, style),
623
+ }
624
+ }
625
+
626
+ type GlobalStyles = `style ${keyof typeof globalStyles}`
627
+ type GlobalPredicate = `predicate ${keyof typeof fakeParsedModel.globals.predicates}`
628
+ type GlobalExpr = GlobalStyles | GlobalPredicate
629
+ export function $global(expr: GlobalExpr): ViewRuleGlobalStyle | ViewRuleGlobalPredicateRef {
630
+ const [_t, id] = expr.split(' ') as [string, string]
631
+ switch (_t) {
632
+ case 'predicate':
633
+ return {
634
+ predicateId: id as GlobalPredicateId,
635
+ }
636
+ case 'style':
637
+ return {
638
+ styleId: id as GlobalStyleID,
639
+ }
640
+ default:
641
+ throw new Error(`Invalid global expression: ${expr}`)
642
+ }
643
+ }
644
+
645
+ export function computeView(
646
+ ...args: [FakeElementIds, ViewRule | ViewRule[]] | [ViewRule | ViewRule[]]
647
+ ) {
648
+ let result: ComputedView
649
+ if (args.length === 1) {
650
+ result = computeElementView(
651
+ fakeModel,
652
+ {
653
+ ...emptyView,
654
+ rules: [args[0]].flat(),
655
+ },
656
+ )
657
+ } else {
658
+ result = computeElementView(
659
+ fakeModel,
660
+ {
661
+ ...emptyView,
662
+ id: 'index' as ViewId,
663
+ viewOf: args[0] as Fqn,
664
+ rules: [args[1]].flat(),
665
+ },
666
+ )
667
+ }
668
+
669
+ result = withReadableEdges(result)
670
+
671
+ return Object.assign(result, {
672
+ nodeIds: map(result.nodes, prop('id')) as string[],
673
+ edgeIds: map(result.edges, prop('id')) as string[],
674
+ })
675
+ }
@@ -0,0 +1,48 @@
1
+ import { type LikeC4Model } from '../../model'
2
+ import type { ConnectionModel } from '../../model/connection/model'
3
+ import type { RelationshipModel } from '../../model/RelationModel'
4
+ import type { AnyAux } from '../../model/types'
5
+ import type { ElementExpression, Expression, RelationExpression } from '../../types/expression'
6
+
7
+ import type { Ctx, Memory, Stage, StageExclude, StageInclude } from './memory'
8
+
9
+ export type Where<T extends {}> = (x: T) => boolean
10
+
11
+ export type Elem = Ctx['Element']
12
+ export type Connection = Ctx['Connection']
13
+
14
+ export type ElementWhere = Where<Elem>
15
+ export type ElementWhereFilter = (elements: readonly Elem[]) => Elem[]
16
+ export type ConnectionWhere = Where<Connection>
17
+ export type RelationshipWhere = Where<RelationshipModel>
18
+ export type ConnectionWhereFilter = (connections: readonly Connection[]) => Connection[]
19
+
20
+ export { Memory } from './memory'
21
+
22
+ export type Connections = ReadonlyArray<ConnectionModel<AnyAux>>
23
+
24
+ export interface PredicateCtx<Expr extends Expression = Expression> {
25
+ expr: Expr
26
+ stage: Stage
27
+ // View scope
28
+ scope: Elem | null
29
+ model: LikeC4Model<AnyAux>
30
+ memory: Memory
31
+ where: Expr extends RelationExpression ? RelationshipWhere
32
+ : Expr extends ElementExpression ? ElementWhere
33
+ : never
34
+ filterWhere: Expr extends RelationExpression ? ConnectionWhereFilter
35
+ : Expr extends ElementExpression ? ElementWhereFilter
36
+ : never
37
+ }
38
+ export interface IncludePredicateCtx<Expr extends Expression = Expression> extends PredicateCtx<Expr> {
39
+ stage: StageInclude
40
+ }
41
+ export interface ExcludePredicateCtx<Expr extends Expression = Expression> extends PredicateCtx<Expr> {
42
+ stage: StageExclude
43
+ }
44
+
45
+ export interface PredicateExecutor<Expr extends Expression> {
46
+ include(ctx: IncludePredicateCtx<Expr>): StageInclude | undefined
47
+ exclude(ctx: ExcludePredicateCtx<Expr>): StageExclude | undefined
48
+ }