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