@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,58 @@
1
+ import { partition } from 'remeda'
2
+ import type { DynamicViewRule, DynamicViewStep, Fqn, ViewId, ViewRulePredicate } from '../../../types'
3
+ import { type FakeElementIds, fakeModel } from '../../element-view/__test__/fixture'
4
+ import { DynamicViewComputeCtx } from '../compute'
5
+
6
+ const emptyView = {
7
+ __: 'dynamic' as const,
8
+ id: 'index' as ViewId,
9
+ title: null,
10
+ description: null,
11
+ tags: null,
12
+ links: null,
13
+ customColorDefinitions: {},
14
+ rules: []
15
+ }
16
+
17
+ type StepExpr = `${FakeElementIds} ${'->' | '<-'} ${FakeElementIds}`
18
+ type StepProps = Omit<DynamicViewStep, 'source' | 'target' | 'isBackward'>
19
+
20
+ export function $step(expr: StepExpr, props?: string | Partial<StepProps>): DynamicViewStep {
21
+ const title = typeof props === 'string' ? props : props?.title
22
+ if (expr.includes(' -> ')) {
23
+ const [source, target] = expr.split(' -> ')
24
+ return {
25
+ source: source as Fqn,
26
+ target: target as Fqn,
27
+ ...(typeof props === 'object' ? props : {}),
28
+ title: title ?? null
29
+ }
30
+ }
31
+ if (expr.includes(' <- ')) {
32
+ const [target, source] = expr.split(' <- ')
33
+ return {
34
+ source: source as Fqn,
35
+ target: target as Fqn,
36
+ ...(typeof props === 'object' ? props : {}),
37
+ title: title ?? null,
38
+ isBackward: true
39
+ }
40
+ }
41
+ throw new Error(`Invalid step expression: ${expr}`)
42
+ }
43
+
44
+ export function compute(stepsAndRules: (DynamicViewStep | ViewRulePredicate)[]) {
45
+ const [steps, rules] = partition(stepsAndRules, (s): s is DynamicViewStep => 'source' in s)
46
+ const view = DynamicViewComputeCtx.compute(
47
+ {
48
+ ...emptyView,
49
+ steps,
50
+ rules: rules as DynamicViewRule[]
51
+ },
52
+ fakeModel
53
+ )
54
+ return Object.assign(view, {
55
+ nodeIds: view.nodes.map((node) => node.id) as string[],
56
+ edgeIds: view.edges.map((edge) => edge.id) as string[]
57
+ })
58
+ }
@@ -0,0 +1,310 @@
1
+ import { filter, findLast, flatMap, hasAtLeast, isTruthy, map, only, pipe, unique } from 'remeda'
2
+ import { nonNullable } from '../../errors'
3
+ import {
4
+ type Color,
5
+ type ComputedDynamicView,
6
+ type ComputedEdge,
7
+ DefaultArrowType,
8
+ DefaultLineStyle,
9
+ DefaultRelationshipColor,
10
+ type DynamicView,
11
+ type DynamicViewStep,
12
+ type Element,
13
+ isDynamicViewParallelSteps,
14
+ isViewRuleAutoLayout,
15
+ isViewRulePredicate,
16
+ type NonEmptyArray,
17
+ type RelationId,
18
+ type RelationshipArrowType,
19
+ type RelationshipLineType,
20
+ type StepEdgeId,
21
+ stepEdgeId,
22
+ type Tag,
23
+ type ViewId
24
+ } from '../../types'
25
+ import { ancestorsFqn, commonAncestor, parentFqn } from '../../utils/fqn'
26
+ import type { LikeC4ModelGraph } from '../LikeC4ModelGraph'
27
+ import { applyCustomElementProperties } from '../utils/applyCustomElementProperties'
28
+ import { applyViewRuleStyles } from '../utils/applyViewRuleStyles'
29
+ import { buildComputedNodesFromElements } from '../utils/buildComputedNodes'
30
+ import { buildElementNotations } from '../utils/buildElementNotations'
31
+ import { elementExprToPredicate } from '../utils/elementExpressionToPredicate'
32
+ import { resolveGlobalRulesInDynamicView } from '../utils/resolve-global-rules'
33
+ import { calcViewLayoutHash } from '../utils/view-hash'
34
+
35
+ export namespace DynamicViewComputeCtx {
36
+ export interface Step {
37
+ id: StepEdgeId
38
+ source: Element
39
+ target: Element
40
+ title: string | null
41
+ description?: string
42
+ technology?: string
43
+ color?: Color
44
+ line?: RelationshipLineType
45
+ head?: RelationshipArrowType
46
+ tail?: RelationshipArrowType
47
+ relations: RelationId[]
48
+ isBackward: boolean
49
+ navigateTo?: ViewId
50
+ tags?: NonEmptyArray<Tag>
51
+ }
52
+ }
53
+
54
+ export class DynamicViewComputeCtx {
55
+ // Intermediate state
56
+ private explicits = new Set<Element>()
57
+ private steps = [] as DynamicViewComputeCtx.Step[]
58
+
59
+ public static compute(view: DynamicView, graph: LikeC4ModelGraph): ComputedDynamicView {
60
+ return new DynamicViewComputeCtx(view, graph).compute()
61
+ }
62
+
63
+ private constructor(
64
+ protected view: DynamicView,
65
+ protected graph: LikeC4ModelGraph
66
+ ) {}
67
+
68
+ private addStep(
69
+ {
70
+ source: stepSource,
71
+ target: stepTarget,
72
+ title: stepTitle,
73
+ isBackward,
74
+ navigateTo: stepNavigateTo,
75
+ ...step
76
+ }: DynamicViewStep,
77
+ index: number,
78
+ parent?: number
79
+ ) {
80
+ const id = parent ? stepEdgeId(parent, index) : stepEdgeId(index)
81
+ const source = this.graph.element(stepSource)
82
+ const target = this.graph.element(stepTarget)
83
+
84
+ this.explicits.add(source)
85
+ this.explicits.add(target)
86
+
87
+ const {
88
+ title,
89
+ relations,
90
+ tags,
91
+ navigateTo: derivedNavigateTo,
92
+ head,
93
+ tail,
94
+ color,
95
+ line,
96
+ notation
97
+ } = this.findRelations(source, target)
98
+
99
+ const navigateTo = isTruthy(stepNavigateTo) && stepNavigateTo !== this.view.id ? stepNavigateTo : derivedNavigateTo
100
+
101
+ this.steps.push({
102
+ id,
103
+ ...step,
104
+ source,
105
+ target,
106
+ title: stepTitle ?? title,
107
+ relations: relations ?? [],
108
+ isBackward: isBackward ?? false,
109
+ ...(navigateTo ? { navigateTo } : {}),
110
+ ...(tags ? { tags } : {}),
111
+ ...(head ? { head } : {}),
112
+ ...(tail ? { tail } : {}),
113
+ ...(color ? { color } : {}),
114
+ ...(line ? { line } : {}),
115
+ ...(notation ? { notation } : {})
116
+ })
117
+ }
118
+
119
+ protected compute(): ComputedDynamicView {
120
+ const {
121
+ docUri: _docUri, // exclude docUri
122
+ rules: _rules, // exclude rules
123
+ steps: viewSteps,
124
+ ...view
125
+ } = this.view
126
+
127
+ let stepNum = 1
128
+ for (const step of viewSteps) {
129
+ if (isDynamicViewParallelSteps(step)) {
130
+ if (step.__parallel.length === 0) {
131
+ continue
132
+ }
133
+ if (step.__parallel.length === 1) {
134
+ this.addStep(step.__parallel[0]!, stepNum)
135
+ } else {
136
+ step.__parallel.forEach((s, i) => this.addStep(s, i + 1, stepNum))
137
+ }
138
+ } else {
139
+ this.addStep(step, stepNum)
140
+ }
141
+ stepNum++
142
+ }
143
+
144
+ const rules = resolveGlobalRulesInDynamicView(this.view, this.graph.globals)
145
+
146
+ for (const rule of rules) {
147
+ if (isViewRulePredicate(rule)) {
148
+ for (const expr of rule.include) {
149
+ const predicate = elementExprToPredicate(expr)
150
+ this.graph.elements.filter(predicate).forEach(e => this.explicits.add(e))
151
+ }
152
+ }
153
+ }
154
+
155
+ const elements = [...this.explicits]
156
+ const nodesMap = buildComputedNodesFromElements(elements)
157
+
158
+ const edges = this.steps.map(({ source, target, relations, title, isBackward, ...step }) => {
159
+ const sourceNode = nonNullable(nodesMap.get(source.id), `Source node ${source.id} not found`)
160
+ const targetNode = nonNullable(nodesMap.get(target.id), `Target node ${target.id} not found`)
161
+ const edge: ComputedEdge = {
162
+ parent: commonAncestor(source.id, target.id),
163
+ source: source.id,
164
+ target: target.id,
165
+ label: title,
166
+ relations,
167
+ color: DefaultRelationshipColor,
168
+ line: DefaultLineStyle,
169
+ head: DefaultArrowType,
170
+ ...step
171
+ }
172
+ if (isBackward) {
173
+ edge.dir = 'back'
174
+ }
175
+
176
+ while (edge.parent && !nodesMap.has(edge.parent)) {
177
+ edge.parent = parentFqn(edge.parent)
178
+ }
179
+ sourceNode.outEdges.push(edge.id)
180
+ targetNode.inEdges.push(edge.id)
181
+ // Process edge source ancestors
182
+ for (const sourceAncestor of ancestorsFqn(edge.source)) {
183
+ if (sourceAncestor === edge.parent) {
184
+ break
185
+ }
186
+ nodesMap.get(sourceAncestor)?.outEdges.push(edge.id)
187
+ }
188
+ // Process target hierarchy
189
+ for (const targetAncestor of ancestorsFqn(edge.target)) {
190
+ if (targetAncestor === edge.parent) {
191
+ break
192
+ }
193
+ nodesMap.get(targetAncestor)?.inEdges.push(edge.id)
194
+ }
195
+ return edge
196
+ })
197
+
198
+ const nodes = applyCustomElementProperties(
199
+ rules,
200
+ applyViewRuleStyles(
201
+ rules,
202
+ // Keep order of elements
203
+ elements.map(e => nonNullable(nodesMap.get(e.id)))
204
+ )
205
+ )
206
+
207
+ const autoLayoutRule = findLast(rules, isViewRuleAutoLayout)
208
+
209
+ const elementNotations = buildElementNotations(nodes)
210
+
211
+ return calcViewLayoutHash({
212
+ ...view,
213
+ autoLayout: {
214
+ direction: autoLayoutRule?.direction ?? 'LR',
215
+ ...(autoLayoutRule?.nodeSep && { nodeSep: autoLayoutRule.nodeSep }),
216
+ ...(autoLayoutRule?.rankSep && { rankSep: autoLayoutRule.rankSep })
217
+ },
218
+ nodes: map(nodes, n => {
219
+ // omit notation
220
+ delete n.notation
221
+ if (n.icon === 'none') {
222
+ delete n.icon
223
+ }
224
+ return n
225
+ }),
226
+ edges,
227
+ ...(elementNotations.length > 0 && {
228
+ notation: {
229
+ elements: elementNotations
230
+ }
231
+ })
232
+ })
233
+ }
234
+
235
+ private findRelations(source: Element, target: Element): {
236
+ title: string | null
237
+ tags: NonEmptyArray<Tag> | null
238
+ relations: NonEmptyArray<RelationId> | null
239
+ navigateTo: ViewId | null
240
+ tail: RelationshipArrowType | null
241
+ head: RelationshipArrowType | null
242
+ color: Color | null
243
+ line: RelationshipLineType | null
244
+ notation: string | null
245
+ } {
246
+ const relationships = unique(this.graph.edgesBetween(source, target).flatMap(e => e.relations))
247
+ if (relationships.length === 0) {
248
+ return {
249
+ title: null,
250
+ tags: null,
251
+ relations: null,
252
+ navigateTo: null,
253
+ tail: null,
254
+ head: null,
255
+ color: null,
256
+ line: null,
257
+ notation: null
258
+ }
259
+ }
260
+ const alltags = pipe(
261
+ relationships,
262
+ flatMap(r => r.tags),
263
+ filter(isTruthy),
264
+ unique()
265
+ )
266
+ const tags = hasAtLeast(alltags, 1) ? alltags : null
267
+ const relations = hasAtLeast(relationships, 1) ? map(relationships, r => r.id) : null
268
+
269
+ // Most closest relation
270
+ const relation = only(relationships) || relationships.find(r => r.source === source.id && r.target === target.id)
271
+
272
+ const navigateTo = !!relation?.navigateTo && relation.navigateTo !== this.view.id ? relation.navigateTo : pipe(
273
+ relationships,
274
+ map(r => r.navigateTo),
275
+ filter(isTruthy),
276
+ filter(v => v !== this.view.id),
277
+ unique(),
278
+ only()
279
+ )
280
+
281
+ const commonProperties = relationships.reduce((acc, r) => {
282
+ isTruthy(r.title) && acc.title.push(r.title)
283
+ isTruthy(r.tail) && acc.tail.push(r.tail)
284
+ isTruthy(r.head) && acc.head.push(r.head)
285
+ isTruthy(r.color) && acc.color.push(r.color)
286
+ isTruthy(r.line) && acc.line.push(r.line)
287
+
288
+ return acc
289
+ }, {
290
+ title: [] as string[],
291
+ tail: [] as RelationshipArrowType[],
292
+ head: [] as RelationshipArrowType[],
293
+ color: [] as Color[],
294
+ line: [] as RelationshipLineType[],
295
+ notation: [] as string[]
296
+ })
297
+
298
+ return {
299
+ tags,
300
+ relations,
301
+ navigateTo: navigateTo ?? null,
302
+ title: pipe(commonProperties.title, unique(), only()) ?? null,
303
+ tail: pipe(commonProperties.tail, unique(), only()) ?? null,
304
+ head: pipe(commonProperties.head, unique(), only()) ?? null,
305
+ color: pipe(commonProperties.color, unique(), only()) ?? null,
306
+ line: pipe(commonProperties.line, unique(), only()) ?? null,
307
+ notation: pipe(commonProperties.notation, unique(), only()) ?? null
308
+ }
309
+ }
310
+ }