@likec4/language-server 1.17.0 → 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 (94) hide show
  1. package/contrib/likec4.tmLanguage.json +1 -1
  2. package/dist/browser.cjs +1 -1
  3. package/dist/browser.d.cts +2 -2
  4. package/dist/browser.d.mts +2 -2
  5. package/dist/browser.d.ts +2 -2
  6. package/dist/browser.mjs +2 -2
  7. package/dist/index.cjs +1 -1
  8. package/dist/index.d.cts +2 -2
  9. package/dist/index.d.mts +2 -2
  10. package/dist/index.d.ts +2 -2
  11. package/dist/index.mjs +2 -2
  12. package/dist/protocol.d.cts +8 -5
  13. package/dist/protocol.d.mts +8 -5
  14. package/dist/protocol.d.ts +8 -5
  15. package/dist/shared/{language-server.DZRuJVSg.cjs → language-server.CO_nmHiL.cjs} +5605 -4215
  16. package/dist/shared/{language-server.DJo88TnT.d.cts → language-server.Da6ey08o.d.cts} +391 -110
  17. package/dist/shared/{language-server.PEjk7U9s.d.ts → language-server.De7S3e5Z.d.ts} +391 -110
  18. package/dist/shared/{language-server.BgDKnNok.d.mts → language-server.Dj4iDjtB.d.mts} +391 -110
  19. package/dist/shared/{language-server.B8qSDsWW.mjs → language-server.oO_9JoAG.mjs} +5594 -4215
  20. package/package.json +17 -31
  21. package/src/Rpc.ts +6 -3
  22. package/src/ast.ts +124 -71
  23. package/src/formatting/LikeC4Formatter.ts +9 -4
  24. package/src/generated/ast.ts +656 -40
  25. package/src/generated/grammar.ts +2 -2
  26. package/src/generated/module.ts +3 -2
  27. package/src/index.ts +1 -0
  28. package/src/like-c4.langium +170 -22
  29. package/src/logger.ts +7 -2
  30. package/src/lsp/CodeLensProvider.ts +0 -1
  31. package/src/lsp/CompletionProvider.ts +17 -2
  32. package/src/lsp/DocumentSymbolProvider.ts +5 -2
  33. package/src/lsp/HoverProvider.ts +34 -2
  34. package/src/lsp/SemanticTokenProvider.ts +58 -32
  35. package/src/model/deployments-index.ts +218 -0
  36. package/src/model/fqn-computation.ts +1 -1
  37. package/src/model/fqn-index.ts +0 -1
  38. package/src/model/index.ts +1 -0
  39. package/src/model/model-builder.ts +172 -37
  40. package/src/model/model-locator.ts +36 -7
  41. package/src/model/model-parser.ts +554 -92
  42. package/src/model-change/changeViewLayout.ts +2 -2
  43. package/src/module.ts +10 -4
  44. package/src/protocol.ts +10 -6
  45. package/src/references/index.ts +1 -0
  46. package/src/references/name-provider.ts +37 -0
  47. package/src/references/scope-computation.ts +130 -21
  48. package/src/references/scope-provider.ts +63 -36
  49. package/src/shared/NodeKindProvider.ts +15 -3
  50. package/src/utils/deploymentRef.ts +31 -0
  51. package/src/{elementRef.ts → utils/elementRef.ts} +1 -1
  52. package/src/utils/stringHash.ts +2 -2
  53. package/src/validation/_shared.ts +7 -5
  54. package/src/validation/deployment-checks.ts +144 -0
  55. package/src/validation/dynamic-view-step.ts +1 -1
  56. package/src/validation/index.ts +7 -0
  57. package/src/validation/relation.ts +1 -1
  58. package/src/validation/view-predicates/deployments.ts +56 -0
  59. package/src/validation/view-predicates/index.ts +1 -0
  60. package/src/view-utils/assignNavigateTo.ts +6 -5
  61. package/src/view-utils/index.ts +0 -1
  62. package/dist/model-graph/index.cjs +0 -10
  63. package/dist/model-graph/index.d.cts +0 -81
  64. package/dist/model-graph/index.d.mts +0 -81
  65. package/dist/model-graph/index.d.ts +0 -81
  66. package/dist/model-graph/index.mjs +0 -1
  67. package/dist/shared/language-server.BGGRJRnr.d.mts +0 -1338
  68. package/dist/shared/language-server.BXFhlTPo.mjs +0 -1953
  69. package/dist/shared/language-server.Bmpq16Gw.d.ts +0 -1338
  70. package/dist/shared/language-server.C1ZfM22X.d.cts +0 -1338
  71. package/dist/shared/language-server.N8HLDQqz.cjs +0 -1967
  72. package/src/model-graph/LikeC4ModelGraph.ts +0 -338
  73. package/src/model-graph/compute-view/__test__/fixture.ts +0 -630
  74. package/src/model-graph/compute-view/compute.ts +0 -788
  75. package/src/model-graph/compute-view/index.ts +0 -33
  76. package/src/model-graph/compute-view/predicates.ts +0 -509
  77. package/src/model-graph/dynamic-view/__test__/fixture.ts +0 -61
  78. package/src/model-graph/dynamic-view/compute.ts +0 -281
  79. package/src/model-graph/dynamic-view/index.ts +0 -29
  80. package/src/model-graph/index.ts +0 -3
  81. package/src/model-graph/utils/applyCustomElementProperties.ts +0 -65
  82. package/src/model-graph/utils/applyCustomRelationProperties.ts +0 -41
  83. package/src/model-graph/utils/applyViewRuleStyles.ts +0 -49
  84. package/src/model-graph/utils/buildComputeNodes.ts +0 -113
  85. package/src/model-graph/utils/buildElementNotations.ts +0 -63
  86. package/src/model-graph/utils/elementExpressionToPredicate.ts +0 -39
  87. package/src/model-graph/utils/relationExpressionToPredicates.ts +0 -43
  88. package/src/model-graph/utils/sortNodes.ts +0 -105
  89. package/src/model-graph/utils/uniqueTags.test.ts +0 -42
  90. package/src/model-graph/utils/uniqueTags.ts +0 -19
  91. package/src/utils/graphlib.ts +0 -9
  92. package/src/view-utils/resolve-extended-views.ts +0 -66
  93. package/src/view-utils/resolve-global-rules.ts +0 -88
  94. package/src/view-utils/view-hash.ts +0 -27
@@ -1,1967 +0,0 @@
1
- 'use strict';
2
-
3
- const core = require('@likec4/core');
4
- const remeda = require('remeda');
5
- const dagre = require('@dagrejs/dagre');
6
- const log = require('@likec4/log');
7
- const objectHash = require('object-hash');
8
-
9
- function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
10
-
11
- const dagre__default = /*#__PURE__*/_interopDefaultCompat(dagre);
12
- const objectHash__default = /*#__PURE__*/_interopDefaultCompat(objectHash);
13
-
14
- function resolveGlobalRulesInElementView(view, globals) {
15
- return view.rules.reduce((acc, rule) => {
16
- if (core.isViewRuleGlobalPredicateRef(rule)) {
17
- const globalPredicates = globals.predicates[rule.predicateId];
18
- if (remeda.isNullish(globalPredicates)) {
19
- log.logger.warn(`Global predicate not found: ${rule.predicateId}`);
20
- return acc;
21
- }
22
- return acc.concat(globalPredicates);
23
- }
24
- if (core.isViewRuleGlobalStyle(rule)) {
25
- const globalStyles = globals.styles[rule.styleId];
26
- if (remeda.isNullish(globalStyles)) {
27
- log.logger.warn(`Global style not found: ${rule.styleId}`);
28
- return acc;
29
- }
30
- return acc.concat(globalStyles);
31
- }
32
- acc.push(rule);
33
- return acc;
34
- }, []);
35
- }
36
- function resolveGlobalRulesInDynamicView(view, globals) {
37
- return view.rules.reduce((acc, rule) => {
38
- if (core.isViewRuleGlobalPredicateRef(rule)) {
39
- const globalPredicates = globals.dynamicPredicates[rule.predicateId];
40
- if (remeda.isNullish(globalPredicates)) {
41
- log.logger.warn(`Global predicate not found: ${rule.predicateId}`);
42
- return acc;
43
- }
44
- return acc.concat(globalPredicates);
45
- }
46
- if (core.isViewRuleGlobalStyle(rule)) {
47
- const globalStyles = globals.styles[rule.styleId];
48
- if (remeda.isNullish(globalStyles)) {
49
- log.logger.warn(`Global style not found: ${rule.styleId}`);
50
- return acc;
51
- }
52
- return acc.concat(globalStyles);
53
- }
54
- acc.push(rule);
55
- return acc;
56
- }, []);
57
- }
58
-
59
- function calcViewLayoutHash(view) {
60
- const tohash = {
61
- id: view.id,
62
- __: view.__ ?? "element",
63
- autoLayout: view.autoLayout,
64
- nodes: remeda.pipe(
65
- view.nodes,
66
- remeda.map(remeda.pick(["id", "title", "description", "technology", "shape", "icon", "children"])),
67
- remeda.mapToObj(({ id, icon, ...node }) => [id, { ...node, icon: remeda.isTruthy(icon) ? "Y" : "N" }])
68
- ),
69
- edges: remeda.pipe(
70
- view.edges,
71
- remeda.map(remeda.pick(["source", "target", "label", "description", "technology", "dir", "head", "tail", "line"])),
72
- remeda.mapToObj(({ source, target, ...edge }) => [`${source}:${target}`, edge])
73
- )
74
- };
75
- view.hash = objectHash__default(tohash, {
76
- ignoreUnknown: true,
77
- respectType: false
78
- });
79
- return view;
80
- }
81
-
82
- function elementExprToPredicate(target) {
83
- if (core.Expr.isElementWhere(target)) {
84
- const predicate = elementExprToPredicate(target.where.expr);
85
- const where = core.whereOperatorAsPredicate(target.where.condition);
86
- return (n) => predicate(n) && where(n);
87
- }
88
- if (core.Expr.isWildcard(target)) {
89
- return () => true;
90
- }
91
- if (core.Expr.isElementKindExpr(target)) {
92
- return target.isEqual ? (n) => n.kind === target.elementKind : (n) => n.kind !== target.elementKind;
93
- }
94
- if (core.Expr.isElementTagExpr(target)) {
95
- return target.isEqual ? ({ tags }) => !!tags && tags.includes(target.elementTag) : ({ tags }) => remeda.isNullish(tags) || !tags.includes(target.elementTag);
96
- }
97
- if (core.Expr.isExpandedElementExpr(target)) {
98
- return (n) => n.id === target.expanded || core.parentFqn(n.id) === target.expanded;
99
- }
100
- if (core.Expr.isElementRef(target)) {
101
- const { element, isDescedants } = target;
102
- return isDescedants ? (n) => n.id.startsWith(element + ".") : (n) => n.id === element;
103
- }
104
- if (core.Expr.isCustomElement(target)) {
105
- return elementExprToPredicate(target.custom.expr);
106
- }
107
- core.nonexhaustive(target);
108
- }
109
-
110
- function flattenGroupRules(guard) {
111
- return (rule) => {
112
- if (core.isViewRuleGroup(rule)) {
113
- return rule.groupRules.flatMap(flattenGroupRules(guard));
114
- }
115
- if (core.isViewRulePredicate(rule)) {
116
- return "include" in rule ? rule.include.filter(guard) : [];
117
- }
118
- return [];
119
- };
120
- }
121
- function applyCustomElementProperties(_rules, _nodes) {
122
- const rules = _rules.flatMap(flattenGroupRules(core.Expr.isCustomElement));
123
- if (rules.length === 0) {
124
- return _nodes;
125
- }
126
- const nodes = [..._nodes];
127
- for (const {
128
- custom: { expr, ...props }
129
- } of rules) {
130
- const { border, opacity, ...rest } = remeda.omitBy(props, remeda.isNullish);
131
- const notEmpty = !remeda.isEmpty(rest);
132
- const satisfies = elementExprToPredicate(expr);
133
- nodes.forEach((node, i) => {
134
- if (core.ComputedNode.isNodesGroup(node) || !satisfies(node)) {
135
- return;
136
- }
137
- if (notEmpty) {
138
- node = {
139
- ...node,
140
- isCustomized: true,
141
- ...rest
142
- };
143
- }
144
- let styleOverride;
145
- if (border !== void 0) {
146
- styleOverride = { border };
147
- }
148
- if (opacity !== void 0) {
149
- styleOverride = { ...styleOverride, opacity };
150
- }
151
- if (styleOverride) {
152
- node = {
153
- ...node,
154
- isCustomized: true,
155
- style: {
156
- ...node.style,
157
- ...styleOverride
158
- }
159
- };
160
- }
161
- nodes[i] = node;
162
- });
163
- }
164
- return nodes;
165
- }
166
-
167
- function relationExpressionToPredicates(expr) {
168
- switch (true) {
169
- case core.Expr.isRelationWhere(expr):
170
- const predicate = relationExpressionToPredicates(expr.where.expr);
171
- const where = core.whereOperatorAsPredicate(expr.where.condition);
172
- return (e) => predicate(e) && where(e);
173
- case core.Expr.isRelation(expr): {
174
- const isSource = elementExprToPredicate(expr.source);
175
- const isTarget = elementExprToPredicate(expr.target);
176
- return (edge) => {
177
- return isSource(edge.source) && isTarget(edge.target) || !!expr.isBidirectional && isSource(edge.target) && isTarget(edge.source);
178
- };
179
- }
180
- case core.Expr.isInOut(expr): {
181
- const isInOut = elementExprToPredicate(expr.inout);
182
- return (edge) => isInOut(edge.source) || isInOut(edge.target);
183
- }
184
- case core.Expr.isIncoming(expr): {
185
- const isTarget = elementExprToPredicate(expr.incoming);
186
- return (edge) => isTarget(edge.target);
187
- }
188
- case core.Expr.isOutgoing(expr): {
189
- const isSource = elementExprToPredicate(expr.outgoing);
190
- return (edge) => isSource(edge.source);
191
- }
192
- default:
193
- core.nonexhaustive(expr);
194
- }
195
- }
196
-
197
- function applyCustomRelationProperties(_rules, nodes, _edges) {
198
- const rules = _rules.flatMap(flattenGroupRules(core.Expr.isCustomRelationExpr));
199
- const edges = Array.from(_edges);
200
- if (rules.length === 0 || edges.length === 0) {
201
- return edges;
202
- }
203
- for (const {
204
- customRelation: { relation, title, ...customprops }
205
- } of rules) {
206
- const props = remeda.omitBy(customprops, remeda.isNullish);
207
- const satisfies = relationExpressionToPredicates(relation);
208
- edges.forEach((edge, i) => {
209
- const source = nodes.find((n) => n.id === edge.source);
210
- const target = nodes.find((n) => n.id === edge.target);
211
- if (!source || !target) {
212
- return;
213
- }
214
- if (satisfies({ source, target, ...remeda.pick(edge, ["kind", "tags"]) })) {
215
- edges[i] = {
216
- ...edge,
217
- ...props,
218
- label: title ?? edge.label,
219
- isCustomized: true
220
- };
221
- }
222
- });
223
- }
224
- return edges;
225
- }
226
-
227
- function applyViewRuleStyles(_rules, nodes) {
228
- const rules = _rules.filter(core.isViewRuleStyle);
229
- if (rules.length === 0) {
230
- return nodes;
231
- }
232
- for (const rule of rules) {
233
- const predicates = [];
234
- for (const target of rule.targets) {
235
- predicates.push(elementExprToPredicate(target));
236
- }
237
- remeda.pipe(
238
- nodes,
239
- remeda.filter(remeda.isNot(core.ComputedNode.isNodesGroup)),
240
- remeda.filter(remeda.anyPass(predicates)),
241
- remeda.forEach((n) => {
242
- n.shape = rule.style.shape ?? n.shape;
243
- n.color = rule.style.color ?? n.color;
244
- if (remeda.isDefined(rule.style.icon)) {
245
- n.icon = rule.style.icon;
246
- }
247
- if (remeda.isDefined(rule.notation)) {
248
- n.notation = rule.notation;
249
- }
250
- let styleOverride;
251
- if (remeda.isDefined(rule.style.border)) {
252
- styleOverride = { border: rule.style.border };
253
- }
254
- if (remeda.isDefined(rule.style.opacity)) {
255
- styleOverride = { ...styleOverride, opacity: rule.style.opacity };
256
- }
257
- if (styleOverride) {
258
- n.style = {
259
- ...n.style,
260
- ...styleOverride
261
- };
262
- }
263
- })
264
- );
265
- }
266
- return nodes;
267
- }
268
-
269
- function updateDepthOfAncestors(node, nodes) {
270
- let parentNd;
271
- while (!!node.parent && (parentNd = nodes.get(node.parent))) {
272
- const depth = parentNd.depth ?? 1;
273
- parentNd.depth = Math.max(depth, (node.depth ?? 0) + 1);
274
- if (parentNd.depth === depth) {
275
- break;
276
- }
277
- node = parentNd;
278
- }
279
- }
280
- function buildComputeNodes(elements, groups) {
281
- const nodesMap = /* @__PURE__ */ new Map();
282
- const elementToGroup = /* @__PURE__ */ new Map();
283
- groups?.forEach(({ id, parent, viewRule, explicits }) => {
284
- if (parent) {
285
- core.nonNullable(nodesMap.get(parent), `Parent group node ${parent} not found`).children.push(id);
286
- }
287
- nodesMap.set(id, {
288
- id,
289
- parent,
290
- kind: core.ElementKind.Group,
291
- title: viewRule.title ?? "",
292
- color: viewRule.color ?? "muted",
293
- shape: "rectangle",
294
- children: [],
295
- inEdges: [],
296
- outEdges: [],
297
- level: 0,
298
- depth: 0,
299
- description: null,
300
- technology: null,
301
- tags: null,
302
- links: null,
303
- style: {
304
- border: viewRule.border ?? "dashed",
305
- opacity: viewRule.opacity ?? 0
306
- }
307
- });
308
- for (const e of explicits) {
309
- elementToGroup.set(e.id, id);
310
- }
311
- });
312
- return Array.from(elements).sort(core.compareByFqnHierarchically).reduce((map, { id, color, shape, style, kind, title, ...el }) => {
313
- let parent = core.parentFqn(id);
314
- let level = 0;
315
- let parentNd;
316
- while (parent) {
317
- parentNd = map.get(parent);
318
- if (parentNd) {
319
- break;
320
- }
321
- parent = core.parentFqn(parent);
322
- }
323
- if (!parentNd && elementToGroup.has(id)) {
324
- parent = elementToGroup.get(id);
325
- parentNd = map.get(parent);
326
- }
327
- if (parentNd) {
328
- if (parentNd.children.length == 0) {
329
- parentNd.depth = 1;
330
- updateDepthOfAncestors(parentNd, map);
331
- }
332
- parentNd.children.push(id);
333
- level = parentNd.level + 1;
334
- }
335
- const node = {
336
- id,
337
- parent,
338
- kind,
339
- title,
340
- color: color ?? core.DefaultThemeColor,
341
- shape: shape ?? core.DefaultElementShape,
342
- children: [],
343
- inEdges: [],
344
- outEdges: [],
345
- level,
346
- ...el,
347
- style: {
348
- ...style
349
- }
350
- };
351
- map.set(id, node);
352
- return map;
353
- }, nodesMap);
354
- }
355
-
356
- function buildElementNotations(nodes) {
357
- return remeda.pipe(
358
- nodes,
359
- remeda.groupBy(remeda.prop("notation")),
360
- remeda.mapValues(
361
- remeda.piped(
362
- remeda.groupBy(remeda.prop("shape")),
363
- remeda.mapValues(
364
- remeda.piped(
365
- remeda.groupBy(remeda.prop("color")),
366
- remeda.mapValues(
367
- remeda.piped(
368
- remeda.map(remeda.prop("kind")),
369
- remeda.unique()
370
- )
371
- ),
372
- remeda.entries(),
373
- remeda.map(([color, kinds]) => ({
374
- kinds,
375
- color
376
- }))
377
- )
378
- ),
379
- remeda.entries(),
380
- remeda.flatMap(
381
- ([shape, colors]) => colors.map(({ color, kinds }) => ({
382
- shape,
383
- color,
384
- kinds
385
- }))
386
- )
387
- )
388
- ),
389
- remeda.entries(),
390
- remeda.flatMap(
391
- ([title, shapes]) => shapes.map(({ shape, color, kinds }) => ({
392
- title,
393
- shape,
394
- color,
395
- kinds
396
- }))
397
- ),
398
- remeda.sortBy(
399
- remeda.prop("shape"),
400
- remeda.prop("title"),
401
- [
402
- (n) => n.kinds.length,
403
- "desc"
404
- ]
405
- )
406
- );
407
- }
408
-
409
- const Graph = dagre__default.graphlib.Graph;
410
- const postorder = dagre__default.graphlib.alg.postorder;
411
- const findCycles = dagre__default.graphlib.alg.findCycles;
412
- const isAcyclic = dagre__default.graphlib.alg.isAcyclic;
413
-
414
- function sortChildren(nodes) {
415
- nodes.forEach((parent) => {
416
- if (parent.children.length > 0) {
417
- parent.children = nodes.flatMap((n) => n.parent === parent.id ? n.id : []);
418
- }
419
- });
420
- }
421
- function sortNodes({
422
- nodes,
423
- edges
424
- }) {
425
- if (nodes.length < 2) {
426
- return nodes;
427
- }
428
- if (edges.length === 0) {
429
- return remeda.pipe(
430
- nodes,
431
- remeda.sort(core.compareByFqnHierarchically),
432
- remeda.tap(sortChildren)
433
- );
434
- }
435
- const g = new Graph({
436
- compound: false,
437
- directed: true,
438
- multigraph: false
439
- });
440
- const getNode = (id) => core.nonNullable(
441
- nodes.find((n) => n.id === id),
442
- "Edge not found"
443
- );
444
- const getEdge = (id) => core.nonNullable(
445
- edges.find((edge) => edge.id === id),
446
- "Edge not found"
447
- );
448
- for (const e of [...edges].sort(core.compareRelations)) {
449
- g.setEdge(e.source, e.target);
450
- }
451
- for (const n of nodes) {
452
- g.setNode(n.id, n.id);
453
- if (n.children.length > 0) {
454
- n.inEdges.forEach((e) => {
455
- const edge = getEdge(e);
456
- if (edge.target !== n.id && getNode(edge.source).children.length === 0) {
457
- g.setEdge(edge.source, n.id);
458
- }
459
- });
460
- }
461
- if (n.parent) {
462
- g.setEdge(n.parent, n.id);
463
- }
464
- }
465
- let sources = g.sources();
466
- if (sources.length === 0) {
467
- sources = remeda.pipe(
468
- nodes,
469
- remeda.filter((n) => n.inEdges.length === 0 || n.parent === null),
470
- remeda.sort(core.compareByFqnHierarchically),
471
- remeda.map((n) => n.id)
472
- );
473
- }
474
- const orderedIds = postorder(g, sources).reverse();
475
- const sorted = orderedIds.map(getNode);
476
- if (sorted.length < nodes.length) {
477
- const unsorted = remeda.difference(nodes, sorted).sort(core.compareByFqnHierarchically);
478
- sorted.push(...unsorted);
479
- }
480
- core.invariant(sorted.length === nodes.length, "Not all nodes were processed by graphlib");
481
- sortChildren(sorted);
482
- return sorted;
483
- }
484
-
485
- function uniqueTags(elements) {
486
- const tags = remeda.pipe(
487
- elements,
488
- remeda.flatMap((e) => e.tags ?? []),
489
- remeda.unique(),
490
- remeda.sort(core.compareNatural)
491
- );
492
- return core.hasAtLeast(tags, 1) ? tags : null;
493
- }
494
-
495
- const NoFilter = () => true;
496
- const Identity = (x) => x;
497
- const filterBy = (pred) => pred === NoFilter ? Identity : remeda.filter(pred);
498
- const filterOne = (pred) => pred === NoFilter ? Identity : (x) => pred(x) ? x : null;
499
- function includeElementRef(expr, where = NoFilter) {
500
- const currentElements = [...this.resolvedElements];
501
- const filter = filterBy(where);
502
- const elements = filter(
503
- expr.isDescedants === true ? this.graph.childrenOrElement(expr.element) : [this.graph.element(expr.element)]
504
- );
505
- if (elements.length === 0) {
506
- return;
507
- }
508
- this.addElement(...elements);
509
- if (elements.length > 1) {
510
- this.addEdges(this.graph.edgesWithin(elements));
511
- }
512
- if (currentElements.length > 0 && elements.length > 0) {
513
- for (const el of elements) {
514
- this.addEdges(this.graph.anyEdgesBetween(el, currentElements));
515
- }
516
- }
517
- }
518
- function excludeElementRef(expr, where = NoFilter) {
519
- const elements = [...this.resolvedElements].filter(remeda.allPass([
520
- elementExprToPredicate(expr),
521
- where
522
- ]));
523
- this.excludeElement(...elements);
524
- }
525
- function includeWildcardRef(_expr, where = NoFilter) {
526
- const root = this.root;
527
- if (!root) {
528
- const elements = this.graph.rootElements.filter(where);
529
- if (elements.length <= 0) {
530
- return;
531
- }
532
- const currentElements2 = [...this.resolvedElements];
533
- this.addElement(...elements);
534
- this.addEdges(this.graph.edgesWithin(elements));
535
- if (currentElements2.length > 0) {
536
- for (const el of elements) {
537
- this.addEdges(this.graph.anyEdgesBetween(el, currentElements2));
538
- }
539
- }
540
- return;
541
- }
542
- const currentElements = [...this.resolvedElements];
543
- const _elRoot = filterOne(where)(this.graph.element(root));
544
- if (_elRoot) {
545
- this.addElement(_elRoot);
546
- }
547
- const filter = filterBy(where);
548
- const children = filter(this.graph.children(root));
549
- const hasChildren = children.length > 0;
550
- if (hasChildren) {
551
- this.addElement(...children);
552
- this.addEdges(this.graph.edgesWithin(children));
553
- } else if (_elRoot) {
554
- children.push(_elRoot);
555
- }
556
- const neighbours = [
557
- ...currentElements,
558
- ...filter([
559
- ...this.graph.siblings(root),
560
- ...this.graph.ancestors(root).flatMap((a) => this.graph.siblings(a.id))
561
- ])
562
- ];
563
- for (const el of children) {
564
- this.addEdges(this.graph.anyEdgesBetween(el, neighbours)).forEach((edge) => {
565
- this.addImplicit(edge.source, edge.target);
566
- });
567
- }
568
- if (!hasChildren && _elRoot) {
569
- const edgesWithSiblings = this.graph.anyEdgesBetween(_elRoot, this.graph.siblings(root));
570
- if (edgesWithSiblings.length === 0) {
571
- const _parentId = core.parentFqn(root);
572
- const parent = _parentId && this.graph.element(_parentId);
573
- if (parent && where(parent)) {
574
- this.addElement(parent);
575
- }
576
- }
577
- }
578
- }
579
- function excludeWildcardRef(_expr, where = NoFilter) {
580
- if (where !== NoFilter) {
581
- const elements = [...this.resolvedElements].filter(where);
582
- this.excludeElement(...elements);
583
- return;
584
- }
585
- const root = this.root;
586
- if (root) {
587
- this.excludeElement(
588
- this.graph.element(root),
589
- ...this.graph.children(root)
590
- );
591
- this.excludeRelation(
592
- ...this.graph.connectedRelations(root)
593
- );
594
- } else {
595
- this.reset();
596
- }
597
- }
598
- function includeExpandedElementExpr(expr, where = NoFilter) {
599
- const filter = filterBy(where);
600
- const currentElements = [...this.resolvedElements];
601
- const parent = this.graph.element(expr.expanded);
602
- if (where(parent)) {
603
- this.addElement(parent);
604
- const anyEdgesBetween = this.graph.anyEdgesBetween(parent, currentElements);
605
- this.addEdges(anyEdgesBetween);
606
- }
607
- const expanded = [];
608
- for (const el of filter(this.graph.children(expr.expanded))) {
609
- this.addImplicit(el);
610
- const edges = this.graph.anyEdgesBetween(el, currentElements);
611
- if (edges.length > 0) {
612
- this.addEdges(edges);
613
- expanded.push(el);
614
- }
615
- }
616
- if (expanded.length > 1) {
617
- this.addEdges(this.graph.edgesWithin(expanded));
618
- }
619
- }
620
- function excludeExpandedElementExpr(expr, where = NoFilter) {
621
- const elements = [...this.resolvedElements].filter(remeda.allPass([
622
- elementExprToPredicate(expr),
623
- where
624
- ]));
625
- this.excludeElement(...elements);
626
- }
627
- const asElementPredicate = (expr) => {
628
- if (expr.isEqual) {
629
- if (core.Expr.isElementKindExpr(expr)) {
630
- return (e) => e.kind === expr.elementKind;
631
- } else {
632
- return ({ tags }) => !!tags && tags.includes(expr.elementTag);
633
- }
634
- } else {
635
- if (core.Expr.isElementKindExpr(expr)) {
636
- return (e) => e.kind !== expr.elementKind;
637
- } else {
638
- return ({ tags }) => remeda.isNullish(tags) || tags.length === 0 || !tags.includes(expr.elementTag);
639
- }
640
- }
641
- };
642
- function includeElementKindOrTag(expr, where = NoFilter) {
643
- const elements = this.graph.elements.filter(asElementPredicate(expr)).filter(where);
644
- if (elements.length > 0) {
645
- const currentElements = [...this.resolvedElements];
646
- this.addElement(...elements);
647
- this.addEdges(this.graph.edgesWithin(elements));
648
- for (const el of elements) {
649
- this.addEdges(this.graph.anyEdgesBetween(el, currentElements));
650
- }
651
- }
652
- }
653
- function excludeElementKindOrTag(expr, where = NoFilter) {
654
- const elements = [...this.resolvedElements].filter(asElementPredicate(expr)).filter(where);
655
- if (elements.length > 0) {
656
- this.excludeElement(...elements);
657
- }
658
- }
659
- function resolveNeighbours(expr) {
660
- if (core.Expr.isElementRef(expr)) {
661
- return this.graph.ascendingSiblings(expr.element);
662
- }
663
- return this.root ? this.graph.ascendingSiblings(this.root) : this.graph.rootElements;
664
- }
665
- function resolveElements(expr) {
666
- if (core.Expr.isWildcard(expr)) {
667
- if (this.root) {
668
- return [...this.graph.children(this.root), this.graph.element(this.root)];
669
- } else {
670
- return this.graph.rootElements;
671
- }
672
- }
673
- if (core.Expr.isElementKindExpr(expr)) {
674
- return this.graph.elements.filter((el) => {
675
- if (expr.isEqual) {
676
- return el.kind === expr.elementKind;
677
- }
678
- return el.kind !== expr.elementKind;
679
- });
680
- }
681
- if (core.Expr.isElementTagExpr(expr)) {
682
- return this.graph.elements.filter((el) => {
683
- const tags = el.tags;
684
- if (expr.isEqual) {
685
- return !!tags && tags.includes(expr.elementTag);
686
- }
687
- return remeda.isNullish(tags) || tags.length === 0 || !tags.includes(expr.elementTag);
688
- });
689
- }
690
- if (core.Expr.isExpandedElementExpr(expr)) {
691
- return [this.graph.element(expr.expanded)];
692
- }
693
- if (!core.Expr.isElementRef(expr)) {
694
- return core.nonexhaustive(expr);
695
- }
696
- if (this.root === expr.element && expr.isDescedants !== true) {
697
- return [...this.graph.children(this.root), this.graph.element(this.root)];
698
- }
699
- if (expr.isDescedants) {
700
- return this.graph.childrenOrElement(expr.element);
701
- } else {
702
- return [this.graph.element(expr.element)];
703
- }
704
- }
705
- function edgesIncomingExpr(expr) {
706
- if (core.Expr.isWildcard(expr)) {
707
- if (!this.root) {
708
- return [];
709
- }
710
- const sources2 = this.graph.ascendingSiblings(this.root);
711
- const targets2 = [...this.graph.children(this.root), this.graph.element(this.root)];
712
- return this.graph.edgesBetween(sources2, targets2);
713
- }
714
- const targets = resolveElements.call(this, expr);
715
- if (targets.length === 0) {
716
- return [];
717
- }
718
- let sources = [...this.resolvedElements];
719
- if (core.Expr.isElementRef(expr) || core.Expr.isExpandedElementExpr(expr)) {
720
- const exprElement = expr.element ?? expr.expanded;
721
- const isDescedants = expr.isDescedants ?? false;
722
- sources = sources.filter(
723
- (el) => (
724
- // allow elements, that are not nested or are direct children
725
- !core.isAncestor(exprElement, el.id) || isDescedants && core.parentFqn(el.id) === exprElement
726
- )
727
- );
728
- }
729
- if (sources.length === 0) {
730
- sources = resolveNeighbours.call(this, expr);
731
- }
732
- return this.graph.edgesBetween(sources, targets);
733
- }
734
- const filterEdges = (edges, where) => {
735
- if (!where) {
736
- return edges;
737
- }
738
- return remeda.pipe(
739
- edges,
740
- remeda.map((e) => ({ ...e, relations: e.relations.filter(where) })),
741
- remeda.filter((e) => e.relations.length > 0)
742
- );
743
- };
744
- const filterRelations = (edges, where) => {
745
- return remeda.pipe(
746
- edges,
747
- remeda.flatMap((e) => e.relations),
748
- where ? remeda.filter(where) : Identity,
749
- remeda.unique()
750
- );
751
- };
752
- function includeIncomingExpr(expr, where) {
753
- const edges = filterEdges(edgesIncomingExpr.call(this, expr.incoming), where);
754
- if (edges.length === 0) {
755
- return;
756
- }
757
- this.addEdges(edges).forEach((edge) => {
758
- this.addImplicit(edge.target);
759
- });
760
- }
761
- function excludeIncomingExpr(expr, where) {
762
- let relations = filterRelations(edgesIncomingExpr.call(this, expr.incoming), where);
763
- this.excludeRelation(...relations);
764
- }
765
- function edgesOutgoingExpr(expr) {
766
- if (core.Expr.isWildcard(expr)) {
767
- if (!this.root) {
768
- return [];
769
- }
770
- const targets2 = this.graph.ascendingSiblings(this.root);
771
- const sources2 = [...this.graph.children(this.root), this.graph.element(this.root)];
772
- return this.graph.edgesBetween(sources2, targets2);
773
- }
774
- const sources = resolveElements.call(this, expr);
775
- if (sources.length === 0) {
776
- return [];
777
- }
778
- let targets = [...this.resolvedElements];
779
- if (core.Expr.isElementRef(expr) || core.Expr.isExpandedElementExpr(expr)) {
780
- const sourceElement = expr.element ?? expr.expanded;
781
- const isDescedants = expr.isDescedants ?? false;
782
- targets = targets.filter(
783
- (el) => (
784
- // allow elements, that are not nested or are direct children
785
- !core.isAncestor(sourceElement, el.id) || isDescedants && core.parentFqn(el.id) === sourceElement
786
- )
787
- );
788
- }
789
- if (targets.length === 0) {
790
- targets = resolveNeighbours.call(this, expr);
791
- }
792
- return this.graph.edgesBetween(sources, targets);
793
- }
794
- function includeOutgoingExpr(expr, where) {
795
- const edges = filterEdges(edgesOutgoingExpr.call(this, expr.outgoing), where);
796
- if (edges.length === 0) {
797
- return;
798
- }
799
- this.addEdges(edges).forEach((edge) => {
800
- this.addImplicit(edge.source);
801
- });
802
- }
803
- function excludeOutgoingExpr(expr, where) {
804
- const relations = filterRelations(edgesOutgoingExpr.call(this, expr.outgoing), where);
805
- this.excludeRelation(...relations);
806
- }
807
- var EdgePredicateResult;
808
- ((EdgePredicateResult2) => {
809
- EdgePredicateResult2.Empty = {
810
- implicits: [],
811
- edges: []
812
- };
813
- })(EdgePredicateResult || (EdgePredicateResult = {}));
814
- function edgesInOutExpr({ inout }, where) {
815
- if (core.Expr.isWildcard(inout)) {
816
- if (!this.root) {
817
- return EdgePredicateResult.Empty;
818
- }
819
- const neighbours = this.graph.ascendingSiblings(this.root);
820
- return {
821
- edges: filterEdges(this.graph.anyEdgesBetween(this.graph.element(this.root), neighbours), where),
822
- implicits: []
823
- };
824
- }
825
- const elements = resolveElements.call(this, inout);
826
- if (elements.length === 0) {
827
- return EdgePredicateResult.Empty;
828
- }
829
- let currentElements = [...this.resolvedElements];
830
- if (core.Expr.isElementRef(inout) || core.Expr.isExpandedElementExpr(inout)) {
831
- const exprElement = inout.element ?? inout.expanded;
832
- const isDescedants = inout.isDescedants ?? false;
833
- currentElements = currentElements.filter(
834
- (el) => (
835
- // allow elements, that are not nested or are direct children
836
- !core.isAncestor(exprElement, el.id) || isDescedants && core.parentFqn(el.id) === exprElement
837
- )
838
- );
839
- }
840
- if (currentElements.length === 0) {
841
- currentElements = resolveNeighbours.call(this, inout);
842
- }
843
- return elements.reduce((acc, el) => {
844
- const edges = filterEdges(this.graph.anyEdgesBetween(el, currentElements), where);
845
- if (edges.length > 0) {
846
- acc.implicits.push(el);
847
- acc.edges.push(...edges);
848
- }
849
- return acc;
850
- }, { implicits: [], edges: [] });
851
- }
852
- function includeInOutExpr(expr, where) {
853
- const { implicits, edges } = edgesInOutExpr.call(this, expr, where);
854
- this.addEdges(edges);
855
- this.addImplicit(...implicits);
856
- }
857
- function excludeInOutExpr(expr, where) {
858
- const { edges } = edgesInOutExpr.call(this, expr, where);
859
- this.excludeRelation(...edges.flatMap((e) => e.relations));
860
- }
861
- function resolveRelationExprElements(expr) {
862
- if (core.Expr.isElementRef(expr) && this.root === expr.element && expr.isDescedants !== true) {
863
- return [...this.graph.children(expr.element), this.graph.element(expr.element)];
864
- }
865
- if (core.Expr.isExpandedElementExpr(expr) && this.root === expr.expanded) {
866
- return [...this.graph.children(expr.expanded), this.graph.element(expr.expanded)];
867
- }
868
- return resolveElements.call(this, expr);
869
- }
870
- function includeRelationExpr(expr, where) {
871
- let sources, targets;
872
- if (core.Expr.isWildcard(expr.source) && !core.Expr.isWildcard(expr.target)) {
873
- sources = resolveNeighbours.call(this, expr.target);
874
- targets = resolveRelationExprElements.call(this, expr.target);
875
- } else if (!core.Expr.isWildcard(expr.source) && core.Expr.isWildcard(expr.target)) {
876
- sources = resolveRelationExprElements.call(this, expr.source);
877
- targets = resolveNeighbours.call(this, expr.source);
878
- } else {
879
- sources = resolveRelationExprElements.call(this, expr.source);
880
- targets = resolveRelationExprElements.call(this, expr.target);
881
- }
882
- const edges = this.graph.edgesBetween(sources, targets);
883
- if (expr.isBidirectional === true) {
884
- edges.push(...this.graph.edgesBetween(targets, sources));
885
- }
886
- this.addEdges(filterEdges(edges, where)).forEach((edge) => {
887
- this.activeGroup.addImplicit(edge.source, edge.target);
888
- });
889
- }
890
- function excludeRelationExpr(expr, where) {
891
- const isSource = elementExprToPredicate(expr.source);
892
- const isTarget = elementExprToPredicate(expr.target);
893
- const satisfies = (edge) => {
894
- let result = isSource(edge.source) && isTarget(edge.target);
895
- if (!result && expr.isBidirectional) {
896
- result = isSource(edge.target) && isTarget(edge.source);
897
- }
898
- return result;
899
- };
900
- const edges = this.edges.filter(satisfies);
901
- const relations = filterRelations(edges, where);
902
- this.excludeRelation(...relations);
903
- }
904
-
905
- function compareEdges(a, b) {
906
- return core.compareRelations(
907
- { source: a.source.id, target: a.target.id },
908
- { source: b.source.id, target: b.target.id }
909
- );
910
- }
911
- class NodesGroup {
912
- constructor(id, viewRule, parent = null) {
913
- this.id = id;
914
- this.viewRule = viewRule;
915
- this.parent = parent;
916
- }
917
- static kind = core.ElementKind.Group;
918
- static root() {
919
- return new NodesGroup("@root", { title: null, groupRules: [] });
920
- }
921
- static is(node) {
922
- return node.kind === NodesGroup.kind;
923
- }
924
- explicits = /* @__PURE__ */ new Set();
925
- implicits = /* @__PURE__ */ new Set();
926
- /**
927
- * Add element explicitly
928
- * Included even without relationships
929
- */
930
- addElement(...el) {
931
- for (const r of el) {
932
- this.explicits.add(r);
933
- this.implicits.add(r);
934
- }
935
- }
936
- /**
937
- * Add element implicitly
938
- * Included if only has relationships
939
- */
940
- addImplicit(...el) {
941
- for (const r of el) {
942
- this.implicits.add(r);
943
- }
944
- }
945
- excludeElement(...excludes) {
946
- for (const el of excludes) {
947
- this.explicits.delete(el);
948
- this.implicits.delete(el);
949
- }
950
- }
951
- isEmpty() {
952
- return this.explicits.size === 0 && this.implicits.size === 0;
953
- }
954
- }
955
- class ComputeCtx {
956
- constructor(view, graph) {
957
- this.view = view;
958
- this.graph = graph;
959
- }
960
- // Intermediate state
961
- explicits = /* @__PURE__ */ new Set();
962
- implicits = /* @__PURE__ */ new Set();
963
- ctxEdges = [];
964
- /**
965
- * Root group - not included in the groups
966
- * But used to accumulate elements that are not in any group
967
- */
968
- __rootGroup = NodesGroup.root();
969
- groups = [];
970
- activeGroupStack = [];
971
- get activeGroup() {
972
- return this.activeGroupStack[0] ?? this.__rootGroup;
973
- }
974
- get includedElements() {
975
- return /* @__PURE__ */ new Set([
976
- ...this.explicits,
977
- ...this.ctxEdges.flatMap((e) => [e.source, e.target])
978
- ]);
979
- }
980
- get resolvedElements() {
981
- return /* @__PURE__ */ new Set([
982
- ...this.explicits,
983
- ...this.implicits,
984
- ...this.ctxEdges.flatMap((e) => [e.source, e.target])
985
- ]);
986
- }
987
- get edges() {
988
- return this.ctxEdges;
989
- }
990
- get root() {
991
- return core.isScopedElementView(this.view) ? this.view.viewOf : null;
992
- }
993
- static elementView(view, graph) {
994
- return new ComputeCtx(view, graph).compute();
995
- }
996
- compute() {
997
- this.reset();
998
- const {
999
- docUri: _docUri,
1000
- // exclude docUri
1001
- rules: _rules,
1002
- // exclude rules
1003
- ...view
1004
- } = this.view;
1005
- const rules = resolveGlobalRulesInElementView(this.view, this.graph.globals);
1006
- const viewPredicates = rules.filter(remeda.anyPass([core.isViewRulePredicate, core.isViewRuleGroup]));
1007
- if (this.root && viewPredicates.length == 0) {
1008
- this.addElement(this.graph.element(this.root));
1009
- }
1010
- this.processPredicates(viewPredicates);
1011
- this.removeRedundantImplicitEdges();
1012
- if (this.groups.length > 0) {
1013
- this.cleanGroupElements();
1014
- }
1015
- const elements = [...this.includedElements];
1016
- const nodesMap = buildComputeNodes(elements, this.groups);
1017
- const edges = this.computeEdges();
1018
- const edgesMap = new Map(edges.map((edge) => [edge.id, edge]));
1019
- this.linkNodesAndEdges(nodesMap, edges);
1020
- let initialSort = elements.map((e) => core.nonNullable(nodesMap.get(e.id), `Node ${e.id} not found in nodesMap`));
1021
- if (this.groups.length > 0) {
1022
- initialSort = remeda.concat(
1023
- this.groups.map((g) => core.nonNullable(nodesMap.get(g.id), `Node ${g.id} not found in nodesMap`)),
1024
- initialSort
1025
- );
1026
- }
1027
- const nodes = applyCustomElementProperties(
1028
- rules,
1029
- applyViewRuleStyles(
1030
- rules,
1031
- // Build graph and apply postorder sort
1032
- sortNodes({
1033
- nodes: initialSort,
1034
- edges
1035
- })
1036
- )
1037
- );
1038
- const sortedEdges = /* @__PURE__ */ new Set([
1039
- ...nodes.flatMap((n) => n.children.length === 0 ? n.outEdges.flatMap((id) => edgesMap.get(id) ?? []) : []),
1040
- ...edges
1041
- ]);
1042
- const autoLayoutRule = this.view.rules.findLast(core.isViewRuleAutoLayout);
1043
- const elementNotations = buildElementNotations(nodes);
1044
- return calcViewLayoutHash({
1045
- ...view,
1046
- autoLayout: {
1047
- direction: autoLayoutRule?.direction ?? "TB",
1048
- ...autoLayoutRule?.nodeSep && { nodeSep: autoLayoutRule.nodeSep },
1049
- ...autoLayoutRule?.rankSep && { rankSep: autoLayoutRule.rankSep }
1050
- },
1051
- nodes: remeda.map(nodes, (n) => {
1052
- delete n.notation;
1053
- if (n.icon === "none") {
1054
- delete n.icon;
1055
- }
1056
- return n;
1057
- }),
1058
- edges: applyCustomRelationProperties(rules, nodes, sortedEdges),
1059
- ...elementNotations.length > 0 && {
1060
- notation: {
1061
- elements: elementNotations
1062
- }
1063
- }
1064
- });
1065
- }
1066
- computeEdges() {
1067
- return this.ctxEdges.map((e) => {
1068
- core.invariant(remeda.hasAtLeast(e.relations, 1), "Edge must have at least one relation");
1069
- const relations = remeda.sort(e.relations, core.compareRelations);
1070
- const source = e.source.id;
1071
- const target = e.target.id;
1072
- const edge = {
1073
- id: `${source}:${target}`,
1074
- parent: core.commonAncestor(source, target),
1075
- source,
1076
- target,
1077
- label: null,
1078
- relations: relations.map((r) => r.id)
1079
- };
1080
- let relation;
1081
- relation = remeda.only(relations) ?? remeda.pipe(
1082
- relations,
1083
- remeda.filter((r) => r.source === source && r.target === target),
1084
- remeda.only()
1085
- );
1086
- if (!relation) {
1087
- const allprops = remeda.pipe(
1088
- relations,
1089
- remeda.reduce((acc, r) => {
1090
- if (remeda.isTruthy(r.title) && !acc.title.includes(r.title)) {
1091
- acc.title.push(r.title);
1092
- }
1093
- if (remeda.isTruthy(r.description) && !acc.description.includes(r.description)) {
1094
- acc.description.push(r.description);
1095
- }
1096
- if (remeda.isTruthy(r.technology) && !acc.technology.includes(r.technology)) {
1097
- acc.technology.push(r.technology);
1098
- }
1099
- if (remeda.isTruthy(r.kind) && !acc.kind.includes(r.kind)) {
1100
- acc.kind.push(r.kind);
1101
- }
1102
- if (remeda.isTruthy(r.color) && !acc.color.includes(r.color)) {
1103
- acc.color.push(r.color);
1104
- }
1105
- if (remeda.isTruthy(r.line) && !acc.line.includes(r.line)) {
1106
- acc.line.push(r.line);
1107
- }
1108
- if (remeda.isTruthy(r.head) && !acc.head.includes(r.head)) {
1109
- acc.head.push(r.head);
1110
- }
1111
- if (remeda.isTruthy(r.tail) && !acc.tail.includes(r.tail)) {
1112
- acc.tail.push(r.tail);
1113
- }
1114
- if (remeda.isTruthy(r.navigateTo) && !acc.navigateTo.includes(r.navigateTo)) {
1115
- acc.navigateTo.push(r.navigateTo);
1116
- }
1117
- return acc;
1118
- }, {
1119
- title: [],
1120
- description: [],
1121
- technology: [],
1122
- kind: [],
1123
- head: [],
1124
- tail: [],
1125
- color: [],
1126
- line: [],
1127
- navigateTo: []
1128
- })
1129
- );
1130
- relation = {
1131
- title: remeda.only(allprops.title) ?? "[...]",
1132
- description: remeda.only(allprops.description),
1133
- technology: remeda.only(allprops.technology),
1134
- kind: remeda.only(allprops.kind),
1135
- head: remeda.only(allprops.head),
1136
- tail: remeda.only(allprops.tail),
1137
- color: remeda.only(allprops.color),
1138
- line: remeda.only(allprops.line),
1139
- navigateTo: remeda.only(allprops.navigateTo)
1140
- };
1141
- }
1142
- const tags = uniqueTags(relations);
1143
- return Object.assign(
1144
- edge,
1145
- this.getEdgeLabel(relation),
1146
- remeda.isTruthy(relation.description) && { description: relation.description },
1147
- remeda.isTruthy(relation.technology) && { technology: relation.technology },
1148
- remeda.isTruthy(relation.kind) && { kind: relation.kind },
1149
- relation.color && { color: relation.color },
1150
- relation.line && { line: relation.line },
1151
- relation.head && { head: relation.head },
1152
- relation.tail && { tail: relation.tail },
1153
- relation.navigateTo && { navigateTo: relation.navigateTo },
1154
- tags && { tags }
1155
- );
1156
- });
1157
- }
1158
- linkNodesAndEdges(nodesMap, edges) {
1159
- for (const edge of edges) {
1160
- const source = nodesMap.get(edge.source);
1161
- const target = nodesMap.get(edge.target);
1162
- core.invariant(source, `Source node ${edge.source} not found`);
1163
- core.invariant(target, `Target node ${edge.target} not found`);
1164
- const sourceAncestors = this.ancestorsOf(source, nodesMap);
1165
- const targetAncestors = this.ancestorsOf(target, nodesMap);
1166
- const edgeParent = remeda.last(
1167
- core.commonHead(
1168
- remeda.reverse(sourceAncestors),
1169
- remeda.reverse(targetAncestors)
1170
- )
1171
- );
1172
- edge.parent = edgeParent?.id ?? null;
1173
- source.outEdges.push(edge.id);
1174
- target.inEdges.push(edge.id);
1175
- for (const sourceAncestor of sourceAncestors) {
1176
- if (sourceAncestor === edgeParent) {
1177
- break;
1178
- }
1179
- sourceAncestor.outEdges.push(edge.id);
1180
- }
1181
- for (const targetAncestor of targetAncestors) {
1182
- if (targetAncestor === edgeParent) {
1183
- break;
1184
- }
1185
- targetAncestor.inEdges.push(edge.id);
1186
- }
1187
- }
1188
- }
1189
- addEdges(edges) {
1190
- const added = [];
1191
- for (const e of edges) {
1192
- if (!remeda.hasAtLeast(e.relations, 1)) {
1193
- continue;
1194
- }
1195
- const existing = this.ctxEdges.find(
1196
- (_e) => _e.source.id === e.source.id && _e.target.id === e.target.id
1197
- );
1198
- if (existing) {
1199
- existing.relations = remeda.unique([...existing.relations, ...e.relations]);
1200
- added.push(existing);
1201
- continue;
1202
- }
1203
- added.push(e);
1204
- this.ctxEdges.push(e);
1205
- }
1206
- return added;
1207
- }
1208
- /**
1209
- * Add element explicitly
1210
- * Included even without relationships
1211
- */
1212
- addElement(...el) {
1213
- for (const r of el) {
1214
- if (!this.explicits.has(r)) {
1215
- this.activeGroup.addElement(r);
1216
- } else if (this.activeGroup !== this.__rootGroup) {
1217
- this.groups.forEach((g) => {
1218
- g.implicits.delete(r);
1219
- });
1220
- this.activeGroup.addImplicit(r);
1221
- }
1222
- this.explicits.add(r);
1223
- this.implicits.add(r);
1224
- }
1225
- }
1226
- /**
1227
- * Add element implicitly
1228
- * Included if only has relationships
1229
- */
1230
- addImplicit(...el) {
1231
- for (const r of el) {
1232
- this.implicits.add(r);
1233
- if (this.activeGroup !== this.__rootGroup) {
1234
- this.groups.forEach((g) => {
1235
- g.implicits.delete(r);
1236
- });
1237
- }
1238
- }
1239
- this.activeGroup.addImplicit(...el);
1240
- }
1241
- excludeElement(...excludes) {
1242
- for (const el of excludes) {
1243
- this.ctxEdges = this.ctxEdges.filter((e) => e.source.id !== el.id && e.target.id !== el.id);
1244
- this.explicits.delete(el);
1245
- this.implicits.delete(el);
1246
- }
1247
- this.__rootGroup.excludeElement(...excludes);
1248
- this.groups.forEach((g) => {
1249
- g.excludeElement(...excludes);
1250
- });
1251
- }
1252
- excludeRelation(...relations) {
1253
- if (relations.length === 0) {
1254
- return;
1255
- }
1256
- const excludedImplicits = /* @__PURE__ */ new Set();
1257
- const ctxEdges = remeda.pipe(
1258
- this.ctxEdges,
1259
- remeda.map((edge) => {
1260
- const edgerelations = edge.relations.filter((r) => !relations.includes(r));
1261
- if (edgerelations.length === 0) {
1262
- excludedImplicits.add(edge.source);
1263
- excludedImplicits.add(edge.target);
1264
- return null;
1265
- }
1266
- if (edgerelations.length !== edge.relations.length) {
1267
- return {
1268
- ...edge,
1269
- relations: edgerelations
1270
- };
1271
- }
1272
- return edge;
1273
- }),
1274
- remeda.filter(remeda.isNonNull)
1275
- );
1276
- this.ctxEdges = ctxEdges;
1277
- const remaining = this.includedElements;
1278
- if (remaining.size === 0) {
1279
- this.implicits.clear();
1280
- this.__rootGroup.implicits.clear();
1281
- this.groups.forEach((g) => g.implicits.clear());
1282
- return;
1283
- }
1284
- for (const el of excludedImplicits) {
1285
- if (!remaining.has(el)) {
1286
- this.implicits.delete(el);
1287
- this.__rootGroup.implicits.delete(el);
1288
- this.groups.forEach((g) => g.implicits.delete(el));
1289
- }
1290
- }
1291
- }
1292
- reset() {
1293
- this.explicits.clear();
1294
- this.implicits.clear();
1295
- this.ctxEdges = [];
1296
- this.__rootGroup = NodesGroup.root();
1297
- this.groups = [];
1298
- this.activeGroupStack = [];
1299
- }
1300
- // Filter out edges if there are edges between descendants
1301
- // i.e. remove implicit edges, derived from childs
1302
- removeRedundantImplicitEdges() {
1303
- const processedRelations = /* @__PURE__ */ new Set();
1304
- const excludeProcessed = (relations) => relations.reduce((acc, rel) => {
1305
- if (!processedRelations.has(rel)) {
1306
- acc.push(rel);
1307
- processedRelations.add(rel);
1308
- }
1309
- return acc;
1310
- }, []);
1311
- const isNestedEdgeOf = (parent) => {
1312
- const { source, target } = parent;
1313
- return (edge) => {
1314
- const isSameSource = source.id === edge.source.id;
1315
- const isSameTarget = target.id === edge.target.id;
1316
- if (isSameSource && isSameTarget) {
1317
- return true;
1318
- }
1319
- const isSourceNested = core.isAncestor(source.id, edge.source.id);
1320
- const isTargetNested = core.isAncestor(target.id, edge.target.id);
1321
- return isSourceNested && isTargetNested || isSameSource && isTargetNested || isSameTarget && isSourceNested;
1322
- };
1323
- };
1324
- const edges = [...this.ctxEdges].sort(compareEdges).reverse();
1325
- this.ctxEdges = edges.reduce((acc, e) => {
1326
- const relations = excludeProcessed(e.relations);
1327
- if (relations.length === 0) {
1328
- return acc;
1329
- }
1330
- if (acc.length > 0 && acc.some(isNestedEdgeOf(e))) {
1331
- return acc;
1332
- }
1333
- acc.push({
1334
- source: e.source,
1335
- target: e.target,
1336
- relations
1337
- });
1338
- return acc;
1339
- }, []);
1340
- }
1341
- cleanGroupElements() {
1342
- const unprocessed = new Set(remeda.difference(
1343
- [...this.includedElements],
1344
- [...this.__rootGroup.explicits]
1345
- ));
1346
- for (const group of this.groups) {
1347
- const explicits = [...group.explicits];
1348
- group.explicits.clear();
1349
- for (const el of explicits) {
1350
- if (unprocessed.delete(el)) {
1351
- group.explicits.add(el);
1352
- }
1353
- }
1354
- }
1355
- for (const group of this.groups) {
1356
- for (const el of group.implicits) {
1357
- if (unprocessed.delete(el)) {
1358
- group.explicits.add(el);
1359
- }
1360
- }
1361
- group.implicits.clear();
1362
- }
1363
- }
1364
- processPredicates(viewRules) {
1365
- for (const rule of viewRules) {
1366
- if (core.isViewRuleGroup(rule)) {
1367
- const parent = remeda.first(this.activeGroupStack);
1368
- const groupId = `@gr${this.groups.length + 1}`;
1369
- const group = new NodesGroup(groupId, rule, parent?.id ?? null);
1370
- this.groups.push(group);
1371
- this.activeGroupStack.unshift(group);
1372
- this.processPredicates(rule.groupRules);
1373
- this.activeGroupStack.shift();
1374
- continue;
1375
- }
1376
- const isInclude = "include" in rule;
1377
- const exprs = rule.include ?? rule.exclude;
1378
- for (const expr of exprs) {
1379
- if (core.Expr.isElementPredicateExpr(expr)) {
1380
- this.processElementPredicate(expr, isInclude);
1381
- continue;
1382
- }
1383
- if (core.Expr.isRelationPredicateExpr(expr)) {
1384
- this.processRelationPredicate(expr, isInclude);
1385
- continue;
1386
- }
1387
- core.nonexhaustive(expr);
1388
- }
1389
- }
1390
- return this;
1391
- }
1392
- processElementPredicate(expr, isInclude, where) {
1393
- if (core.Expr.isCustomElement(expr)) {
1394
- if (isInclude) {
1395
- this.processElementPredicate(expr.custom.expr, isInclude);
1396
- }
1397
- return this;
1398
- }
1399
- if (core.Expr.isElementWhere(expr)) {
1400
- const where2 = core.whereOperatorAsPredicate(expr.where.condition);
1401
- this.processElementPredicate(expr.where.expr, isInclude, where2);
1402
- return this;
1403
- }
1404
- if (core.Expr.isExpandedElementExpr(expr)) {
1405
- isInclude ? includeExpandedElementExpr.call(this, expr, where) : excludeExpandedElementExpr.call(this, expr, where);
1406
- return this;
1407
- }
1408
- if (core.Expr.isElementKindExpr(expr) || core.Expr.isElementTagExpr(expr)) {
1409
- isInclude ? includeElementKindOrTag.call(this, expr, where) : excludeElementKindOrTag.call(this, expr, where);
1410
- return this;
1411
- }
1412
- if (core.Expr.isElementRef(expr)) {
1413
- isInclude ? includeElementRef.call(this, expr, where) : excludeElementRef.call(this, expr, where);
1414
- return this;
1415
- }
1416
- if (core.Expr.isWildcard(expr)) {
1417
- isInclude ? includeWildcardRef.call(this, expr, where) : excludeWildcardRef.call(this, expr, where);
1418
- return this;
1419
- }
1420
- core.nonexhaustive(expr);
1421
- }
1422
- processRelationPredicate(expr, isInclude, where) {
1423
- if (core.Expr.isCustomRelationExpr(expr)) {
1424
- if (isInclude) {
1425
- this.processRelationPredicate(expr.customRelation.relation, isInclude);
1426
- }
1427
- return this;
1428
- }
1429
- if (core.Expr.isRelationWhere(expr)) {
1430
- const where2 = core.whereOperatorAsPredicate(expr.where.condition);
1431
- this.processRelationPredicate(expr.where.expr, isInclude, where2);
1432
- return this;
1433
- }
1434
- if (core.Expr.isIncoming(expr)) {
1435
- isInclude ? includeIncomingExpr.call(this, expr, where) : excludeIncomingExpr.call(this, expr, where);
1436
- return this;
1437
- }
1438
- if (core.Expr.isOutgoing(expr)) {
1439
- isInclude ? includeOutgoingExpr.call(this, expr, where) : excludeOutgoingExpr.call(this, expr, where);
1440
- return this;
1441
- }
1442
- if (core.Expr.isInOut(expr)) {
1443
- isInclude ? includeInOutExpr.call(this, expr, where) : excludeInOutExpr.call(this, expr, where);
1444
- return this;
1445
- }
1446
- if (core.Expr.isRelation(expr)) {
1447
- isInclude ? includeRelationExpr.call(this, expr, where) : excludeRelationExpr.call(this, expr, where);
1448
- return this;
1449
- }
1450
- core.nonexhaustive(expr);
1451
- }
1452
- ancestorsOf(node, nodesMap) {
1453
- const ancestors = [];
1454
- let parent = node.parent;
1455
- while (parent) {
1456
- const parentNode = nodesMap.get(parent);
1457
- if (!parentNode) {
1458
- break;
1459
- }
1460
- ancestors.push(parentNode);
1461
- parent = parentNode.parent;
1462
- }
1463
- return ancestors;
1464
- }
1465
- getEdgeLabel(relation) {
1466
- const labelParts = [];
1467
- if (remeda.isTruthy(relation.title)) {
1468
- labelParts.push(relation.title);
1469
- }
1470
- if (remeda.isTruthy(relation.technology)) {
1471
- labelParts.push(`[${relation.technology}]`);
1472
- }
1473
- return labelParts.length > 0 ? { label: labelParts.join("\n") } : {};
1474
- }
1475
- }
1476
-
1477
- function computeElementView(view, graph) {
1478
- return ComputeCtx.elementView(view, graph);
1479
- }
1480
- function computeView(view, graph) {
1481
- try {
1482
- return {
1483
- isSuccess: true,
1484
- view: computeElementView(view, graph)
1485
- };
1486
- } catch (e) {
1487
- return {
1488
- isSuccess: false,
1489
- error: e instanceof Error ? e : new Error(`Unknown error: ${e}`),
1490
- view: void 0
1491
- };
1492
- }
1493
- }
1494
-
1495
- class DynamicViewComputeCtx {
1496
- constructor(view, graph) {
1497
- this.view = view;
1498
- this.graph = graph;
1499
- }
1500
- // Intermediate state
1501
- explicits = /* @__PURE__ */ new Set();
1502
- steps = [];
1503
- static compute(view, graph) {
1504
- return new DynamicViewComputeCtx(view, graph).compute();
1505
- }
1506
- addStep({
1507
- source: stepSource,
1508
- target: stepTarget,
1509
- title: stepTitle,
1510
- isBackward,
1511
- navigateTo: stepNavigateTo,
1512
- ...step
1513
- }, index, parent) {
1514
- const id = parent ? core.StepEdgeId(parent, index) : core.StepEdgeId(index);
1515
- const source = this.graph.element(stepSource);
1516
- const target = this.graph.element(stepTarget);
1517
- this.explicits.add(source);
1518
- this.explicits.add(target);
1519
- const {
1520
- title,
1521
- relations,
1522
- tags,
1523
- navigateTo: derivedNavigateTo
1524
- } = this.findRelations(source, target);
1525
- const navigateTo = remeda.isTruthy(stepNavigateTo) && stepNavigateTo !== this.view.id ? stepNavigateTo : derivedNavigateTo;
1526
- this.steps.push({
1527
- id,
1528
- ...step,
1529
- source,
1530
- target,
1531
- title: stepTitle ?? title,
1532
- relations: relations ?? [],
1533
- isBackward: isBackward ?? false,
1534
- ...navigateTo ? { navigateTo } : {},
1535
- ...tags ? { tags } : {}
1536
- });
1537
- }
1538
- compute() {
1539
- const {
1540
- docUri: _docUri,
1541
- // exclude docUri
1542
- rules: _rules,
1543
- // exclude rules
1544
- steps: viewSteps,
1545
- ...view
1546
- } = this.view;
1547
- let stepNum = 1;
1548
- for (const step of viewSteps) {
1549
- if (core.isDynamicViewParallelSteps(step)) {
1550
- if (step.__parallel.length === 0) {
1551
- continue;
1552
- }
1553
- if (step.__parallel.length === 1) {
1554
- this.addStep(step.__parallel[0], stepNum);
1555
- } else {
1556
- step.__parallel.forEach((s, i) => this.addStep(s, i + 1, stepNum));
1557
- }
1558
- } else {
1559
- this.addStep(step, stepNum);
1560
- }
1561
- stepNum++;
1562
- }
1563
- const rules = resolveGlobalRulesInDynamicView(this.view, this.graph.globals);
1564
- for (const rule of rules) {
1565
- if (core.isDynamicViewIncludeRule(rule)) {
1566
- for (const expr of rule.include) {
1567
- const predicate = elementExprToPredicate(expr);
1568
- this.graph.elements.filter(predicate).forEach((e) => this.explicits.add(e));
1569
- }
1570
- }
1571
- }
1572
- const elements = [...this.explicits];
1573
- const nodesMap = buildComputeNodes(elements);
1574
- const edges = this.steps.map(({ source, target, relations, title, isBackward, ...step }) => {
1575
- const sourceNode = core.nonNullable(nodesMap.get(source.id), `Source node ${source.id} not found`);
1576
- const targetNode = core.nonNullable(nodesMap.get(target.id), `Target node ${target.id} not found`);
1577
- const edge = {
1578
- parent: core.commonAncestor(source.id, target.id),
1579
- source: source.id,
1580
- target: target.id,
1581
- label: title,
1582
- relations,
1583
- color: core.DefaultRelationshipColor,
1584
- line: core.DefaultLineStyle,
1585
- head: core.DefaultArrowType,
1586
- ...step
1587
- };
1588
- if (isBackward) {
1589
- edge.dir = "back";
1590
- }
1591
- while (edge.parent && !nodesMap.has(edge.parent)) {
1592
- edge.parent = core.parentFqn(edge.parent);
1593
- }
1594
- sourceNode.outEdges.push(edge.id);
1595
- targetNode.inEdges.push(edge.id);
1596
- for (const sourceAncestor of core.ancestorsFqn(edge.source)) {
1597
- if (sourceAncestor === edge.parent) {
1598
- break;
1599
- }
1600
- nodesMap.get(sourceAncestor)?.outEdges.push(edge.id);
1601
- }
1602
- for (const targetAncestor of core.ancestorsFqn(edge.target)) {
1603
- if (targetAncestor === edge.parent) {
1604
- break;
1605
- }
1606
- nodesMap.get(targetAncestor)?.inEdges.push(edge.id);
1607
- }
1608
- return edge;
1609
- });
1610
- const nodes = applyCustomElementProperties(
1611
- rules,
1612
- applyViewRuleStyles(
1613
- rules,
1614
- // Keep order of elements
1615
- elements.map((e) => core.nonNullable(nodesMap.get(e.id)))
1616
- )
1617
- );
1618
- const autoLayoutRule = rules.findLast(core.isViewRuleAutoLayout);
1619
- const elementNotations = buildElementNotations(nodes);
1620
- return calcViewLayoutHash({
1621
- ...view,
1622
- autoLayout: {
1623
- direction: autoLayoutRule?.direction ?? "LR",
1624
- ...autoLayoutRule?.nodeSep && { nodeSep: autoLayoutRule.nodeSep },
1625
- ...autoLayoutRule?.rankSep && { rankSep: autoLayoutRule.rankSep }
1626
- },
1627
- nodes: remeda.map(nodes, (n) => {
1628
- delete n.notation;
1629
- if (n.icon === "none") {
1630
- delete n.icon;
1631
- }
1632
- return n;
1633
- }),
1634
- edges,
1635
- ...elementNotations.length > 0 && {
1636
- notation: {
1637
- elements: elementNotations
1638
- }
1639
- }
1640
- });
1641
- }
1642
- findRelations(source, target) {
1643
- const relationships = remeda.unique(this.graph.edgesBetween(source, target).flatMap((e) => e.relations));
1644
- if (relationships.length === 0) {
1645
- return {
1646
- title: null,
1647
- tags: null,
1648
- relations: null,
1649
- navigateTo: null
1650
- };
1651
- }
1652
- const alltags = remeda.pipe(
1653
- relationships,
1654
- remeda.flatMap((r) => r.tags),
1655
- remeda.filter(remeda.isTruthy),
1656
- remeda.unique()
1657
- );
1658
- const tags = remeda.hasAtLeast(alltags, 1) ? alltags : null;
1659
- const relations = remeda.hasAtLeast(relationships, 1) ? remeda.map(relationships, (r) => r.id) : null;
1660
- const relation = remeda.only(relationships) || relationships.find((r) => r.source === source.id && r.target === target.id);
1661
- const title = remeda.isTruthy(relation?.title) ? relation.title : remeda.pipe(
1662
- relationships,
1663
- remeda.map((r) => r.title),
1664
- remeda.filter(remeda.isTruthy),
1665
- remeda.unique(),
1666
- remeda.only()
1667
- );
1668
- const navigateTo = !!relation?.navigateTo && relation.navigateTo !== this.view.id ? relation.navigateTo : remeda.pipe(
1669
- relationships,
1670
- remeda.map((r) => r.navigateTo),
1671
- remeda.filter(remeda.isTruthy),
1672
- remeda.filter((v) => v !== this.view.id),
1673
- remeda.unique(),
1674
- remeda.only()
1675
- );
1676
- return {
1677
- title: title ?? null,
1678
- tags,
1679
- relations,
1680
- navigateTo: navigateTo ?? null
1681
- };
1682
- }
1683
- }
1684
-
1685
- function computeDynamicView(view, graph) {
1686
- try {
1687
- return {
1688
- isSuccess: true,
1689
- view: DynamicViewComputeCtx.compute(view, graph)
1690
- };
1691
- } catch (e) {
1692
- return {
1693
- isSuccess: false,
1694
- error: e instanceof Error ? e : new Error(`Unknown error: ${e}`),
1695
- view: void 0
1696
- };
1697
- }
1698
- }
1699
-
1700
- const RelationsSet = Set;
1701
- const MapRelations = Map;
1702
- function intersection(a, b) {
1703
- if (a.size === 0 || b.size === 0) {
1704
- return /* @__PURE__ */ new Set();
1705
- }
1706
- return new Set([...a].filter((value) => b.has(value)));
1707
- }
1708
- class LikeC4ModelGraph {
1709
- #elements = /* @__PURE__ */ new Map();
1710
- // Parent element for given FQN
1711
- #parents = /* @__PURE__ */ new Map();
1712
- // Children elements for given FQN
1713
- #children = /* @__PURE__ */ new Map();
1714
- #rootElements = /* @__PURE__ */ new Set();
1715
- #relations = /* @__PURE__ */ new Map();
1716
- // Incoming to an element or its descendants
1717
- #incoming = new MapRelations();
1718
- // Outgoing from an element or its descendants
1719
- #outgoing = new MapRelations();
1720
- // Relationships inside the element, among descendants
1721
- #internal = new MapRelations();
1722
- #cacheAscendingSiblings = /* @__PURE__ */ new Map();
1723
- globals;
1724
- constructor({ elements, relations, globals }) {
1725
- this.globals = globals ?? {
1726
- predicates: {},
1727
- dynamicPredicates: {},
1728
- styles: {}
1729
- };
1730
- for (const el of Object.values(elements)) {
1731
- this.addElement(el);
1732
- }
1733
- for (const rel of Object.values(relations)) {
1734
- this.addRelation(rel);
1735
- }
1736
- }
1737
- get rootElements() {
1738
- return [...this.#rootElements];
1739
- }
1740
- get elements() {
1741
- return [...this.#elements.values()];
1742
- }
1743
- element(id) {
1744
- const el = this.#elements.get(id);
1745
- core.invariant(el, `Element ${id} not found`);
1746
- return el;
1747
- }
1748
- connectedRelations(id) {
1749
- return [...this._incomingTo(id), ...this._outgoingFrom(id), ...this._internalOf(id)];
1750
- }
1751
- children(id) {
1752
- return this._childrenOf(id).slice();
1753
- }
1754
- // Get children or element itself if no children
1755
- childrenOrElement(id) {
1756
- const children = this.children(id);
1757
- return children.length > 0 ? children : [this.element(id)];
1758
- }
1759
- // Get all sibling (i.e. same parent)
1760
- siblings(element) {
1761
- const id = remeda.isString(element) ? element : element.id;
1762
- const parent = core.parentFqn(id);
1763
- const siblings = parent ? this._childrenOf(parent) : this.rootElements;
1764
- return siblings.filter((e) => e.id !== id);
1765
- }
1766
- /**
1767
- * Get all ancestor elements (i.e. parent, parent’s parent, etc.)
1768
- * (from closest to root)
1769
- */
1770
- ancestors(element) {
1771
- let id = remeda.isString(element) ? element : element.id;
1772
- const result = [];
1773
- let parent;
1774
- while (parent = this.#parents.get(id)) {
1775
- result.push(parent);
1776
- id = parent.id;
1777
- }
1778
- return result;
1779
- }
1780
- /**
1781
- * Resolve siblings of the element and its ancestors
1782
- * (from closest to root)
1783
- */
1784
- ascendingSiblings(element) {
1785
- const id = remeda.isString(element) ? element : element.id;
1786
- let siblings = this.#cacheAscendingSiblings.get(id);
1787
- if (!siblings) {
1788
- siblings = [
1789
- ...this.siblings(id),
1790
- ...this.ancestors(id).flatMap((a) => this.siblings(a.id))
1791
- ];
1792
- this.#cacheAscendingSiblings.set(id, siblings);
1793
- }
1794
- return siblings.slice();
1795
- }
1796
- /**
1797
- * Resolve all RelationEdges between element and others (any direction)
1798
- */
1799
- anyEdgesBetween(_element, others) {
1800
- if (others.length === 0) {
1801
- return [];
1802
- }
1803
- const element = remeda.isString(_element) ? this.element(_element) : _element;
1804
- const in_element = this._incomingTo(element.id);
1805
- const element_out = this._outgoingFrom(element.id);
1806
- if (in_element.size === 0 && element_out.size === 0) {
1807
- return [];
1808
- }
1809
- const result = [];
1810
- for (const _other of others) {
1811
- const other = remeda.isString(_other) ? this.element(_other) : _other;
1812
- if (core.isSameHierarchy(element, other)) {
1813
- continue;
1814
- }
1815
- if (element_out.size > 0) {
1816
- const outcoming = intersection(this._incomingTo(other.id), element_out);
1817
- if (outcoming.size > 0) {
1818
- result.push({
1819
- source: element,
1820
- target: other,
1821
- relations: [...outcoming]
1822
- });
1823
- }
1824
- }
1825
- if (in_element.size > 0) {
1826
- const incoming = intersection(this._outgoingFrom(other.id), in_element);
1827
- if (incoming.size > 0) {
1828
- result.push({
1829
- source: other,
1830
- target: element,
1831
- relations: [...incoming]
1832
- });
1833
- }
1834
- }
1835
- }
1836
- return result;
1837
- }
1838
- /**
1839
- * Resolve all RelationEdges between elements (any direction)
1840
- */
1841
- edgesWithin(elements) {
1842
- if (elements.length < 2) {
1843
- return [];
1844
- }
1845
- return elements.reduce((acc, el, index, array) => {
1846
- if (index === array.length - 1) {
1847
- return acc;
1848
- }
1849
- acc.push(...this.anyEdgesBetween(el, array.slice(index + 1)));
1850
- return acc;
1851
- }, []);
1852
- }
1853
- /**
1854
- * Get directed RelationEdge between source and target if exists
1855
- */
1856
- edgesBetween(_sources, _targets) {
1857
- const sources = remeda.isArray(_sources) ? _sources : [_sources];
1858
- const targets = remeda.isArray(_targets) ? _targets : [_targets];
1859
- if (sources.length === 0 || targets.length === 0) {
1860
- return [];
1861
- }
1862
- const result = [];
1863
- for (const _source of sources) {
1864
- const source = remeda.isString(_source) ? this.element(_source) : _source;
1865
- const outcoming = this._outgoingFrom(source.id);
1866
- if (outcoming.size === 0) {
1867
- continue;
1868
- }
1869
- for (const _target of targets) {
1870
- const target = remeda.isString(_target) ? this.element(_target) : _target;
1871
- if (core.isSameHierarchy(source, target)) {
1872
- continue;
1873
- }
1874
- const incoming = this._incomingTo(target.id);
1875
- const relations = intersection(outcoming, incoming);
1876
- if (relations.size > 0) {
1877
- result.push({
1878
- source,
1879
- target,
1880
- relations: [...relations]
1881
- });
1882
- }
1883
- }
1884
- }
1885
- return result;
1886
- }
1887
- addElement(el) {
1888
- if (this.#elements.has(el.id)) {
1889
- throw new Error(`Element ${el.id} already exists`);
1890
- }
1891
- this.#elements.set(el.id, el);
1892
- const parentId = core.parentFqn(el.id);
1893
- if (parentId) {
1894
- this.#parents.set(el.id, this.element(parentId));
1895
- this._childrenOf(parentId).push(el);
1896
- } else {
1897
- this.#rootElements.add(el);
1898
- }
1899
- }
1900
- addRelation(rel) {
1901
- if (this.#relations.has(rel.id)) {
1902
- throw new Error(`Relation ${rel.id} already exists`);
1903
- }
1904
- this.#relations.set(rel.id, rel);
1905
- this._incomingTo(rel.target).add(rel);
1906
- this._outgoingFrom(rel.source).add(rel);
1907
- const relParent = core.commonAncestor(rel.source, rel.target);
1908
- if (relParent) {
1909
- for (const ancestor of [relParent, ...core.ancestorsFqn(relParent)]) {
1910
- this._internalOf(ancestor).add(rel);
1911
- }
1912
- }
1913
- for (const sourceAncestor of core.ancestorsFqn(rel.source)) {
1914
- if (sourceAncestor === relParent) {
1915
- break;
1916
- }
1917
- this._outgoingFrom(sourceAncestor).add(rel);
1918
- }
1919
- for (const targetAncestor of core.ancestorsFqn(rel.target)) {
1920
- if (targetAncestor === relParent) {
1921
- break;
1922
- }
1923
- this._incomingTo(targetAncestor).add(rel);
1924
- }
1925
- }
1926
- _childrenOf(id) {
1927
- let children = this.#children.get(id);
1928
- if (!children) {
1929
- children = [];
1930
- this.#children.set(id, children);
1931
- }
1932
- return children;
1933
- }
1934
- _incomingTo(id) {
1935
- let incoming = this.#incoming.get(id);
1936
- if (!incoming) {
1937
- incoming = new RelationsSet();
1938
- this.#incoming.set(id, incoming);
1939
- }
1940
- return incoming;
1941
- }
1942
- _outgoingFrom(id) {
1943
- let outgoing = this.#outgoing.get(id);
1944
- if (!outgoing) {
1945
- outgoing = new RelationsSet();
1946
- this.#outgoing.set(id, outgoing);
1947
- }
1948
- return outgoing;
1949
- }
1950
- _internalOf(id) {
1951
- let internal = this.#internal.get(id);
1952
- if (!internal) {
1953
- internal = new RelationsSet();
1954
- this.#internal.set(id, internal);
1955
- }
1956
- return internal;
1957
- }
1958
- }
1959
-
1960
- exports.Graph = Graph;
1961
- exports.LikeC4ModelGraph = LikeC4ModelGraph;
1962
- exports.computeDynamicView = computeDynamicView;
1963
- exports.computeElementView = computeElementView;
1964
- exports.computeView = computeView;
1965
- exports.findCycles = findCycles;
1966
- exports.isAcyclic = isAcyclic;
1967
- exports.postorder = postorder;