@likec4/language-server 1.15.1 → 1.16.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 (43) hide show
  1. package/contrib/likec4.tmLanguage.json +1 -1
  2. package/dist/browser.cjs +1 -1
  3. package/dist/browser.d.cts +2 -2
  4. package/dist/browser.d.mts +2 -2
  5. package/dist/browser.d.ts +2 -2
  6. package/dist/browser.mjs +2 -2
  7. package/dist/index.cjs +1 -1
  8. package/dist/index.d.cts +2 -2
  9. package/dist/index.d.mts +2 -2
  10. package/dist/index.d.ts +2 -2
  11. package/dist/index.mjs +2 -2
  12. package/dist/model-graph/index.cjs +1 -1
  13. package/dist/model-graph/index.d.cts +4 -2
  14. package/dist/model-graph/index.d.mts +4 -2
  15. package/dist/model-graph/index.d.ts +4 -2
  16. package/dist/model-graph/index.mjs +1 -1
  17. package/dist/shared/language-server.7iILaJYc.d.ts +1338 -0
  18. package/dist/shared/{language-server.B8s2wfT_.cjs → language-server.Bd3NZ8uH.cjs} +123 -72
  19. package/dist/shared/{language-server.BT4WTbFI.mjs → language-server.C5gxpVUH.mjs} +125 -74
  20. package/dist/shared/language-server.CmBZHwSl.d.cts +1338 -0
  21. package/dist/shared/{language-server.U2piOAVt.d.cts → language-server.CnkCWVtf.d.cts} +44 -10
  22. package/dist/shared/{language-server.DfMwkd2l.d.mts → language-server.DIaiY0-C.d.mts} +44 -10
  23. package/dist/shared/language-server.DViE1Zxi.d.mts +1338 -0
  24. package/dist/shared/{language-server.BuChFlda.mjs → language-server.D_13fWJQ.mjs} +177 -84
  25. package/dist/shared/{language-server.j-ShR6as.d.ts → language-server.DwyQ1FtY.d.ts} +44 -10
  26. package/dist/shared/{language-server.DfjkvknB.cjs → language-server.Dym6GL4P.cjs} +175 -82
  27. package/package.json +7 -7
  28. package/src/ast.ts +11 -3
  29. package/src/generated/ast.ts +111 -10
  30. package/src/generated/grammar.ts +1 -1
  31. package/src/like-c4.langium +19 -0
  32. package/src/model/model-builder.ts +25 -30
  33. package/src/model/model-parser.ts +91 -18
  34. package/src/model-graph/LikeC4ModelGraph.ts +22 -11
  35. package/src/model-graph/compute-view/__test__/fixture.ts +63 -19
  36. package/src/model-graph/compute-view/compute.ts +77 -72
  37. package/src/model-graph/dynamic-view/compute.ts +4 -1
  38. package/src/model-graph/utils/applyViewRuleStyles.ts +0 -4
  39. package/src/references/scope-computation.ts +10 -0
  40. package/src/validation/index.ts +3 -0
  41. package/src/validation/specification.ts +21 -0
  42. package/src/view-utils/index.ts +0 -1
  43. package/src/view-utils/resolve-global-rules.ts +66 -50
@@ -3,6 +3,7 @@
3
3
  const core = require('@likec4/core');
4
4
  const remeda = require('remeda');
5
5
  const dagre = require('@dagrejs/dagre');
6
+ const log = require('@likec4/log');
6
7
  const objectHash = require('object-hash');
7
8
 
8
9
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
@@ -10,6 +11,51 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
10
11
  const dagre__default = /*#__PURE__*/_interopDefaultCompat(dagre);
11
12
  const objectHash__default = /*#__PURE__*/_interopDefaultCompat(objectHash);
12
13
 
14
+ function resolveGlobalRulesInElementView(view, globals) {
15
+ return view.rules.reduce((acc, rule) => {
16
+ if (core.isViewRuleGlobalPredicateRef(rule)) {
17
+ const globalPredicates = globals.predicates[rule.predicateId];
18
+ if (remeda.isNullish(globalPredicates)) {
19
+ log.logger.warn(`Global predicate not found: ${rule.predicateId}`);
20
+ return acc;
21
+ }
22
+ return acc.concat(globalPredicates);
23
+ }
24
+ if (core.isViewRuleGlobalStyle(rule)) {
25
+ const globalStyles = globals.styles[rule.styleId];
26
+ if (remeda.isNullish(globalStyles)) {
27
+ log.logger.warn(`Global style not found: ${rule.styleId}`);
28
+ return acc;
29
+ }
30
+ return acc.concat(globalStyles);
31
+ }
32
+ acc.push(rule);
33
+ return acc;
34
+ }, []);
35
+ }
36
+ function resolveGlobalRulesInDynamicView(view, globals) {
37
+ return view.rules.reduce((acc, rule) => {
38
+ if (core.isViewRuleGlobalPredicateRef(rule)) {
39
+ const globalPredicates = globals.dynamicPredicates[rule.predicateId];
40
+ if (remeda.isNullish(globalPredicates)) {
41
+ log.logger.warn(`Global predicate not found: ${rule.predicateId}`);
42
+ return acc;
43
+ }
44
+ return acc.concat(globalPredicates);
45
+ }
46
+ if (core.isViewRuleGlobalStyle(rule)) {
47
+ const globalStyles = globals.styles[rule.styleId];
48
+ if (remeda.isNullish(globalStyles)) {
49
+ log.logger.warn(`Global style not found: ${rule.styleId}`);
50
+ return acc;
51
+ }
52
+ return acc.concat(globalStyles);
53
+ }
54
+ acc.push(rule);
55
+ return acc;
56
+ }, []);
57
+ }
58
+
13
59
  function calcViewLayoutHash(view) {
14
60
  const tohash = {
15
61
  id: view.id,
@@ -186,10 +232,6 @@ function applyViewRuleStyles(_rules, nodes) {
186
232
  for (const rule of rules) {
187
233
  const predicates = [];
188
234
  for (const target of rule.targets) {
189
- if (core.Expr.isWildcard(target)) {
190
- predicates.push(() => true);
191
- break;
192
- }
193
235
  predicates.push(elementExprToPredicate(target));
194
236
  }
195
237
  remeda.pipe(
@@ -929,6 +971,25 @@ class ComputeCtx {
929
971
  get activeGroup() {
930
972
  return this.activeGroupStack[0] ?? this.__rootGroup;
931
973
  }
974
+ get includedElements() {
975
+ return /* @__PURE__ */ new Set([
976
+ ...this.explicits,
977
+ ...this.ctxEdges.flatMap((e) => [e.source, e.target])
978
+ ]);
979
+ }
980
+ get resolvedElements() {
981
+ return /* @__PURE__ */ new Set([
982
+ ...this.explicits,
983
+ ...this.implicits,
984
+ ...this.ctxEdges.flatMap((e) => [e.source, e.target])
985
+ ]);
986
+ }
987
+ get edges() {
988
+ return this.ctxEdges;
989
+ }
990
+ get root() {
991
+ return core.isScopedElementView(this.view) ? this.view.viewOf : null;
992
+ }
932
993
  static elementView(view, graph) {
933
994
  return new ComputeCtx(view, graph).compute();
934
995
  }
@@ -937,9 +998,11 @@ class ComputeCtx {
937
998
  const {
938
999
  docUri: _docUri,
939
1000
  // exclude docUri
940
- rules,
1001
+ rules: _rules,
1002
+ // exclude rules
941
1003
  ...view
942
1004
  } = this.view;
1005
+ const rules = resolveGlobalRulesInElementView(this.view, this.graph.globals);
943
1006
  const viewPredicates = rules.filter(remeda.anyPass([core.isViewRulePredicate, core.isViewRuleGroup]));
944
1007
  if (this.root && viewPredicates.length == 0) {
945
1008
  this.addElement(this.graph.element(this.root));
@@ -951,51 +1014,9 @@ class ComputeCtx {
951
1014
  }
952
1015
  const elements = [...this.includedElements];
953
1016
  const nodesMap = buildComputeNodes(elements, this.groups);
954
- const ancestorsOf = (node) => {
955
- const ancestors = [];
956
- let parent = node.parent;
957
- while (parent) {
958
- const parentNode = nodesMap.get(parent);
959
- if (!parentNode) {
960
- break;
961
- }
962
- ancestors.push(parentNode);
963
- parent = parentNode.parent;
964
- }
965
- return ancestors;
966
- };
967
- const edgesMap = /* @__PURE__ */ new Map();
968
1017
  const edges = this.computeEdges();
969
- for (const edge of edges) {
970
- edgesMap.set(edge.id, edge);
971
- const source = nodesMap.get(edge.source);
972
- const target = nodesMap.get(edge.target);
973
- core.invariant(source, `Source node ${edge.source} not found`);
974
- core.invariant(target, `Target node ${edge.target} not found`);
975
- const sourceAncestors = ancestorsOf(source);
976
- const targetAncestors = ancestorsOf(target);
977
- const edgeParent = remeda.last(
978
- core.commonHead(
979
- remeda.reverse(sourceAncestors),
980
- remeda.reverse(targetAncestors)
981
- )
982
- );
983
- edge.parent = edgeParent?.id ?? null;
984
- source.outEdges.push(edge.id);
985
- target.inEdges.push(edge.id);
986
- for (const sourceAncestor of sourceAncestors) {
987
- if (sourceAncestor === edgeParent) {
988
- break;
989
- }
990
- sourceAncestor.outEdges.push(edge.id);
991
- }
992
- for (const targetAncestor of targetAncestors) {
993
- if (targetAncestor === edgeParent) {
994
- break;
995
- }
996
- targetAncestor.inEdges.push(edge.id);
997
- }
998
- }
1018
+ const edgesMap = new Map(edges.map((edge) => [edge.id, edge]));
1019
+ this.linkNodesAndEdges(nodesMap, edges);
999
1020
  let initialSort = elements.map((e) => core.nonNullable(nodesMap.get(e.id), `Node ${e.id} not found in nodesMap`));
1000
1021
  if (this.groups.length > 0) {
1001
1022
  initialSort = remeda.concat(
@@ -1036,9 +1057,6 @@ class ComputeCtx {
1036
1057
  }
1037
1058
  });
1038
1059
  }
1039
- get root() {
1040
- return core.isScopedElementView(this.view) ? this.view.viewOf : null;
1041
- }
1042
1060
  computeEdges() {
1043
1061
  return this.ctxEdges.map((e) => {
1044
1062
  core.invariant(remeda.hasAtLeast(e.relations, 1), "Edge must have at least one relation");
@@ -1131,21 +1149,36 @@ class ComputeCtx {
1131
1149
  );
1132
1150
  });
1133
1151
  }
1134
- get includedElements() {
1135
- return /* @__PURE__ */ new Set([
1136
- ...this.explicits,
1137
- ...this.ctxEdges.flatMap((e) => [e.source, e.target])
1138
- ]);
1139
- }
1140
- get resolvedElements() {
1141
- return /* @__PURE__ */ new Set([
1142
- ...this.explicits,
1143
- ...this.implicits,
1144
- ...this.ctxEdges.flatMap((e) => [e.source, e.target])
1145
- ]);
1146
- }
1147
- get edges() {
1148
- return this.ctxEdges;
1152
+ linkNodesAndEdges(nodesMap, edges) {
1153
+ for (const edge of edges) {
1154
+ const source = nodesMap.get(edge.source);
1155
+ const target = nodesMap.get(edge.target);
1156
+ core.invariant(source, `Source node ${edge.source} not found`);
1157
+ core.invariant(target, `Target node ${edge.target} not found`);
1158
+ const sourceAncestors = this.ancestorsOf(source, nodesMap);
1159
+ const targetAncestors = this.ancestorsOf(target, nodesMap);
1160
+ const edgeParent = remeda.last(
1161
+ core.commonHead(
1162
+ remeda.reverse(sourceAncestors),
1163
+ remeda.reverse(targetAncestors)
1164
+ )
1165
+ );
1166
+ edge.parent = edgeParent?.id ?? null;
1167
+ source.outEdges.push(edge.id);
1168
+ target.inEdges.push(edge.id);
1169
+ for (const sourceAncestor of sourceAncestors) {
1170
+ if (sourceAncestor === edgeParent) {
1171
+ break;
1172
+ }
1173
+ sourceAncestor.outEdges.push(edge.id);
1174
+ }
1175
+ for (const targetAncestor of targetAncestors) {
1176
+ if (targetAncestor === edgeParent) {
1177
+ break;
1178
+ }
1179
+ targetAncestor.inEdges.push(edge.id);
1180
+ }
1181
+ }
1149
1182
  }
1150
1183
  addEdges(edges) {
1151
1184
  const added = [];
@@ -1234,9 +1267,6 @@ class ComputeCtx {
1234
1267
  }),
1235
1268
  remeda.filter(remeda.isNonNull)
1236
1269
  );
1237
- if (excludedImplicits.size === 0) {
1238
- return;
1239
- }
1240
1270
  this.ctxEdges = ctxEdges;
1241
1271
  const remaining = this.includedElements;
1242
1272
  if (remaining.size === 0) {
@@ -1413,6 +1443,19 @@ class ComputeCtx {
1413
1443
  }
1414
1444
  core.nonexhaustive(expr);
1415
1445
  }
1446
+ ancestorsOf(node, nodesMap) {
1447
+ const ancestors = [];
1448
+ let parent = node.parent;
1449
+ while (parent) {
1450
+ const parentNode = nodesMap.get(parent);
1451
+ if (!parentNode) {
1452
+ break;
1453
+ }
1454
+ ancestors.push(parentNode);
1455
+ parent = parentNode.parent;
1456
+ }
1457
+ return ancestors;
1458
+ }
1416
1459
  getEdgeLabel(relation) {
1417
1460
  const labelParts = [];
1418
1461
  if (remeda.isTruthy(relation.title)) {
@@ -1490,7 +1533,8 @@ class DynamicViewComputeCtx {
1490
1533
  const {
1491
1534
  docUri: _docUri,
1492
1535
  // exclude docUri
1493
- rules,
1536
+ rules: _rules,
1537
+ // exclude rules
1494
1538
  steps: viewSteps,
1495
1539
  ...view
1496
1540
  } = this.view;
@@ -1510,6 +1554,7 @@ class DynamicViewComputeCtx {
1510
1554
  }
1511
1555
  stepNum++;
1512
1556
  }
1557
+ const rules = resolveGlobalRulesInDynamicView(this.view, this.graph.globals);
1513
1558
  for (const rule of rules) {
1514
1559
  if (core.isDynamicViewIncludeRule(rule)) {
1515
1560
  for (const expr of rule.include) {
@@ -1663,7 +1708,13 @@ class LikeC4ModelGraph {
1663
1708
  // Relationships inside the element, among descendants
1664
1709
  #internal = new MapRelations();
1665
1710
  #cacheAscendingSiblings = /* @__PURE__ */ new Map();
1666
- constructor({ elements, relations }) {
1711
+ globals;
1712
+ constructor({ elements, relations, globals }) {
1713
+ this.globals = globals ?? {
1714
+ predicates: {},
1715
+ dynamicPredicates: {},
1716
+ styles: {}
1717
+ };
1667
1718
  for (const el of Object.values(elements)) {
1668
1719
  this.addElement(el);
1669
1720
  }
@@ -1,8 +1,54 @@
1
- import { Expr, whereOperatorAsPredicate, parentFqn, nonexhaustive, ComputedNode, isViewRuleGroup, isViewRulePredicate, isViewRuleStyle, nonNullable, ElementKind, compareByFqnHierarchically, DefaultThemeColor, DefaultElementShape, compareRelations, invariant, compareNatural, hasAtLeast, isAncestor, commonHead, isViewRuleAutoLayout, isScopedElementView, commonAncestor, StepEdgeId, isDynamicViewParallelSteps, isDynamicViewIncludeRule, DefaultRelationshipColor, DefaultLineStyle, DefaultArrowType, ancestorsFqn, isSameHierarchy } from '@likec4/core';
2
- import { pipe, map, pick, mapToObj, isTruthy, isNullish, omitBy, isEmpty, filter, isNot, anyPass, forEach, isDefined, groupBy, prop, mapValues, piped, unique, entries, flatMap, sortBy, sort, tap, difference, allPass, last, reverse, concat, omit, hasAtLeast as hasAtLeast$1, only, reduce, isNonNull, first, isString, isArray } from 'remeda';
1
+ import { isViewRuleGlobalPredicateRef, isViewRuleGlobalStyle, Expr, whereOperatorAsPredicate, parentFqn, nonexhaustive, ComputedNode, isViewRuleGroup, isViewRulePredicate, isViewRuleStyle, nonNullable, ElementKind, compareByFqnHierarchically, DefaultThemeColor, DefaultElementShape, compareRelations, invariant, compareNatural, hasAtLeast, isAncestor, isScopedElementView, isViewRuleAutoLayout, commonAncestor, commonHead, StepEdgeId, isDynamicViewParallelSteps, isDynamicViewIncludeRule, DefaultRelationshipColor, DefaultLineStyle, DefaultArrowType, ancestorsFqn, isSameHierarchy } from '@likec4/core';
2
+ import { isNullish, pipe, map, pick, mapToObj, isTruthy, omitBy, isEmpty, filter, isNot, anyPass, forEach, isDefined, groupBy, prop, mapValues, piped, unique, entries, flatMap, sortBy, sort, tap, difference, allPass, concat, omit, hasAtLeast as hasAtLeast$1, only, reduce, last, reverse, isNonNull, first, isString, isArray } from 'remeda';
3
3
  import dagre from '@dagrejs/dagre';
4
+ import { logger } from '@likec4/log';
4
5
  import objectHash from 'object-hash';
5
6
 
7
+ function resolveGlobalRulesInElementView(view, globals) {
8
+ return view.rules.reduce((acc, rule) => {
9
+ if (isViewRuleGlobalPredicateRef(rule)) {
10
+ const globalPredicates = globals.predicates[rule.predicateId];
11
+ if (isNullish(globalPredicates)) {
12
+ logger.warn(`Global predicate not found: ${rule.predicateId}`);
13
+ return acc;
14
+ }
15
+ return acc.concat(globalPredicates);
16
+ }
17
+ if (isViewRuleGlobalStyle(rule)) {
18
+ const globalStyles = globals.styles[rule.styleId];
19
+ if (isNullish(globalStyles)) {
20
+ logger.warn(`Global style not found: ${rule.styleId}`);
21
+ return acc;
22
+ }
23
+ return acc.concat(globalStyles);
24
+ }
25
+ acc.push(rule);
26
+ return acc;
27
+ }, []);
28
+ }
29
+ function resolveGlobalRulesInDynamicView(view, globals) {
30
+ return view.rules.reduce((acc, rule) => {
31
+ if (isViewRuleGlobalPredicateRef(rule)) {
32
+ const globalPredicates = globals.dynamicPredicates[rule.predicateId];
33
+ if (isNullish(globalPredicates)) {
34
+ logger.warn(`Global predicate not found: ${rule.predicateId}`);
35
+ return acc;
36
+ }
37
+ return acc.concat(globalPredicates);
38
+ }
39
+ if (isViewRuleGlobalStyle(rule)) {
40
+ const globalStyles = globals.styles[rule.styleId];
41
+ if (isNullish(globalStyles)) {
42
+ logger.warn(`Global style not found: ${rule.styleId}`);
43
+ return acc;
44
+ }
45
+ return acc.concat(globalStyles);
46
+ }
47
+ acc.push(rule);
48
+ return acc;
49
+ }, []);
50
+ }
51
+
6
52
  function calcViewLayoutHash(view) {
7
53
  const tohash = {
8
54
  id: view.id,
@@ -179,10 +225,6 @@ function applyViewRuleStyles(_rules, nodes) {
179
225
  for (const rule of rules) {
180
226
  const predicates = [];
181
227
  for (const target of rule.targets) {
182
- if (Expr.isWildcard(target)) {
183
- predicates.push(() => true);
184
- break;
185
- }
186
228
  predicates.push(elementExprToPredicate(target));
187
229
  }
188
230
  pipe(
@@ -922,6 +964,25 @@ class ComputeCtx {
922
964
  get activeGroup() {
923
965
  return this.activeGroupStack[0] ?? this.__rootGroup;
924
966
  }
967
+ get includedElements() {
968
+ return /* @__PURE__ */ new Set([
969
+ ...this.explicits,
970
+ ...this.ctxEdges.flatMap((e) => [e.source, e.target])
971
+ ]);
972
+ }
973
+ get resolvedElements() {
974
+ return /* @__PURE__ */ new Set([
975
+ ...this.explicits,
976
+ ...this.implicits,
977
+ ...this.ctxEdges.flatMap((e) => [e.source, e.target])
978
+ ]);
979
+ }
980
+ get edges() {
981
+ return this.ctxEdges;
982
+ }
983
+ get root() {
984
+ return isScopedElementView(this.view) ? this.view.viewOf : null;
985
+ }
925
986
  static elementView(view, graph) {
926
987
  return new ComputeCtx(view, graph).compute();
927
988
  }
@@ -930,9 +991,11 @@ class ComputeCtx {
930
991
  const {
931
992
  docUri: _docUri,
932
993
  // exclude docUri
933
- rules,
994
+ rules: _rules,
995
+ // exclude rules
934
996
  ...view
935
997
  } = this.view;
998
+ const rules = resolveGlobalRulesInElementView(this.view, this.graph.globals);
936
999
  const viewPredicates = rules.filter(anyPass([isViewRulePredicate, isViewRuleGroup]));
937
1000
  if (this.root && viewPredicates.length == 0) {
938
1001
  this.addElement(this.graph.element(this.root));
@@ -944,51 +1007,9 @@ class ComputeCtx {
944
1007
  }
945
1008
  const elements = [...this.includedElements];
946
1009
  const nodesMap = buildComputeNodes(elements, this.groups);
947
- const ancestorsOf = (node) => {
948
- const ancestors = [];
949
- let parent = node.parent;
950
- while (parent) {
951
- const parentNode = nodesMap.get(parent);
952
- if (!parentNode) {
953
- break;
954
- }
955
- ancestors.push(parentNode);
956
- parent = parentNode.parent;
957
- }
958
- return ancestors;
959
- };
960
- const edgesMap = /* @__PURE__ */ new Map();
961
1010
  const edges = this.computeEdges();
962
- for (const edge of edges) {
963
- edgesMap.set(edge.id, edge);
964
- const source = nodesMap.get(edge.source);
965
- const target = nodesMap.get(edge.target);
966
- invariant(source, `Source node ${edge.source} not found`);
967
- invariant(target, `Target node ${edge.target} not found`);
968
- const sourceAncestors = ancestorsOf(source);
969
- const targetAncestors = ancestorsOf(target);
970
- const edgeParent = last(
971
- commonHead(
972
- reverse(sourceAncestors),
973
- reverse(targetAncestors)
974
- )
975
- );
976
- edge.parent = edgeParent?.id ?? null;
977
- source.outEdges.push(edge.id);
978
- target.inEdges.push(edge.id);
979
- for (const sourceAncestor of sourceAncestors) {
980
- if (sourceAncestor === edgeParent) {
981
- break;
982
- }
983
- sourceAncestor.outEdges.push(edge.id);
984
- }
985
- for (const targetAncestor of targetAncestors) {
986
- if (targetAncestor === edgeParent) {
987
- break;
988
- }
989
- targetAncestor.inEdges.push(edge.id);
990
- }
991
- }
1011
+ const edgesMap = new Map(edges.map((edge) => [edge.id, edge]));
1012
+ this.linkNodesAndEdges(nodesMap, edges);
992
1013
  let initialSort = elements.map((e) => nonNullable(nodesMap.get(e.id), `Node ${e.id} not found in nodesMap`));
993
1014
  if (this.groups.length > 0) {
994
1015
  initialSort = concat(
@@ -1029,9 +1050,6 @@ class ComputeCtx {
1029
1050
  }
1030
1051
  });
1031
1052
  }
1032
- get root() {
1033
- return isScopedElementView(this.view) ? this.view.viewOf : null;
1034
- }
1035
1053
  computeEdges() {
1036
1054
  return this.ctxEdges.map((e) => {
1037
1055
  invariant(hasAtLeast$1(e.relations, 1), "Edge must have at least one relation");
@@ -1124,21 +1142,36 @@ class ComputeCtx {
1124
1142
  );
1125
1143
  });
1126
1144
  }
1127
- get includedElements() {
1128
- return /* @__PURE__ */ new Set([
1129
- ...this.explicits,
1130
- ...this.ctxEdges.flatMap((e) => [e.source, e.target])
1131
- ]);
1132
- }
1133
- get resolvedElements() {
1134
- return /* @__PURE__ */ new Set([
1135
- ...this.explicits,
1136
- ...this.implicits,
1137
- ...this.ctxEdges.flatMap((e) => [e.source, e.target])
1138
- ]);
1139
- }
1140
- get edges() {
1141
- return this.ctxEdges;
1145
+ linkNodesAndEdges(nodesMap, edges) {
1146
+ for (const edge of edges) {
1147
+ const source = nodesMap.get(edge.source);
1148
+ const target = nodesMap.get(edge.target);
1149
+ invariant(source, `Source node ${edge.source} not found`);
1150
+ invariant(target, `Target node ${edge.target} not found`);
1151
+ const sourceAncestors = this.ancestorsOf(source, nodesMap);
1152
+ const targetAncestors = this.ancestorsOf(target, nodesMap);
1153
+ const edgeParent = last(
1154
+ commonHead(
1155
+ reverse(sourceAncestors),
1156
+ reverse(targetAncestors)
1157
+ )
1158
+ );
1159
+ edge.parent = edgeParent?.id ?? null;
1160
+ source.outEdges.push(edge.id);
1161
+ target.inEdges.push(edge.id);
1162
+ for (const sourceAncestor of sourceAncestors) {
1163
+ if (sourceAncestor === edgeParent) {
1164
+ break;
1165
+ }
1166
+ sourceAncestor.outEdges.push(edge.id);
1167
+ }
1168
+ for (const targetAncestor of targetAncestors) {
1169
+ if (targetAncestor === edgeParent) {
1170
+ break;
1171
+ }
1172
+ targetAncestor.inEdges.push(edge.id);
1173
+ }
1174
+ }
1142
1175
  }
1143
1176
  addEdges(edges) {
1144
1177
  const added = [];
@@ -1227,9 +1260,6 @@ class ComputeCtx {
1227
1260
  }),
1228
1261
  filter(isNonNull)
1229
1262
  );
1230
- if (excludedImplicits.size === 0) {
1231
- return;
1232
- }
1233
1263
  this.ctxEdges = ctxEdges;
1234
1264
  const remaining = this.includedElements;
1235
1265
  if (remaining.size === 0) {
@@ -1406,6 +1436,19 @@ class ComputeCtx {
1406
1436
  }
1407
1437
  nonexhaustive(expr);
1408
1438
  }
1439
+ ancestorsOf(node, nodesMap) {
1440
+ const ancestors = [];
1441
+ let parent = node.parent;
1442
+ while (parent) {
1443
+ const parentNode = nodesMap.get(parent);
1444
+ if (!parentNode) {
1445
+ break;
1446
+ }
1447
+ ancestors.push(parentNode);
1448
+ parent = parentNode.parent;
1449
+ }
1450
+ return ancestors;
1451
+ }
1409
1452
  getEdgeLabel(relation) {
1410
1453
  const labelParts = [];
1411
1454
  if (isTruthy(relation.title)) {
@@ -1483,7 +1526,8 @@ class DynamicViewComputeCtx {
1483
1526
  const {
1484
1527
  docUri: _docUri,
1485
1528
  // exclude docUri
1486
- rules,
1529
+ rules: _rules,
1530
+ // exclude rules
1487
1531
  steps: viewSteps,
1488
1532
  ...view
1489
1533
  } = this.view;
@@ -1503,6 +1547,7 @@ class DynamicViewComputeCtx {
1503
1547
  }
1504
1548
  stepNum++;
1505
1549
  }
1550
+ const rules = resolveGlobalRulesInDynamicView(this.view, this.graph.globals);
1506
1551
  for (const rule of rules) {
1507
1552
  if (isDynamicViewIncludeRule(rule)) {
1508
1553
  for (const expr of rule.include) {
@@ -1656,7 +1701,13 @@ class LikeC4ModelGraph {
1656
1701
  // Relationships inside the element, among descendants
1657
1702
  #internal = new MapRelations();
1658
1703
  #cacheAscendingSiblings = /* @__PURE__ */ new Map();
1659
- constructor({ elements, relations }) {
1704
+ globals;
1705
+ constructor({ elements, relations, globals }) {
1706
+ this.globals = globals ?? {
1707
+ predicates: {},
1708
+ dynamicPredicates: {},
1709
+ styles: {}
1710
+ };
1660
1711
  for (const el of Object.values(elements)) {
1661
1712
  this.addElement(el);
1662
1713
  }