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