@webpieces/nx-webpieces-rules 0.4.881 → 0.4.883

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/README.md +40 -13
  2. package/package.json +7 -7
  3. package/src/configs/tag-truth-configs.d.ts +15 -0
  4. package/src/configs/tag-truth-configs.js +20 -1
  5. package/src/configs/tag-truth-configs.js.map +1 -1
  6. package/src/executors/generate/executor.js +3 -0
  7. package/src/executors/generate/executor.js.map +1 -1
  8. package/src/index.d.ts +6 -3
  9. package/src/index.js +15 -1
  10. package/src/index.js.map +1 -1
  11. package/src/lib/graph-color-modes.d.ts +50 -6
  12. package/src/lib/graph-color-modes.js +88 -8
  13. package/src/lib/graph-color-modes.js.map +1 -1
  14. package/src/lib/graph-comparator.js +2 -0
  15. package/src/lib/graph-comparator.js.map +1 -1
  16. package/src/lib/graph-entry-levels.d.ts +35 -0
  17. package/src/lib/graph-entry-levels.js +60 -0
  18. package/src/lib/graph-entry-levels.js.map +1 -0
  19. package/src/lib/graph-impact.d.ts +5 -5
  20. package/src/lib/graph-impact.js +5 -5
  21. package/src/lib/graph-impact.js.map +1 -1
  22. package/src/lib/graph-legend.d.ts +11 -1
  23. package/src/lib/graph-legend.js +44 -6
  24. package/src/lib/graph-legend.js.map +1 -1
  25. package/src/lib/graph-level-bands.js +3 -1
  26. package/src/lib/graph-level-bands.js.map +1 -1
  27. package/src/lib/graph-loader.d.ts +7 -1
  28. package/src/lib/graph-loader.js +10 -1
  29. package/src/lib/graph-loader.js.map +1 -1
  30. package/src/lib/graph-metadata.d.ts +2 -0
  31. package/src/lib/graph-metadata.js +19 -0
  32. package/src/lib/graph-metadata.js.map +1 -1
  33. package/src/lib/graph-page-shell.d.ts +28 -6
  34. package/src/lib/graph-page-shell.js +55 -18
  35. package/src/lib/graph-page-shell.js.map +1 -1
  36. package/src/lib/graph-page-styles.js +3 -1
  37. package/src/lib/graph-page-styles.js.map +1 -1
  38. package/src/lib/graph-products.d.ts +79 -0
  39. package/src/lib/graph-products.js +199 -0
  40. package/src/lib/graph-products.js.map +1 -0
  41. package/src/lib/graph-render-model.d.ts +13 -2
  42. package/src/lib/graph-render-model.js +36 -4
  43. package/src/lib/graph-render-model.js.map +1 -1
  44. package/src/lib/graph-sorter.d.ts +13 -0
  45. package/src/lib/graph-sorter.js.map +1 -1
  46. package/src/lib/graph-visualizer.client.d.ts +20 -10
  47. package/src/lib/graph-visualizer.client.js +67 -31
  48. package/src/lib/graph-visualizer.client.js.map +1 -1
  49. package/src/lib/graph-visualizer.js +7 -6
  50. package/src/lib/graph-visualizer.js.map +1 -1
  51. package/src/lib/product-resolver.d.ts +38 -0
  52. package/src/lib/product-resolver.js +63 -0
  53. package/src/lib/product-resolver.js.map +1 -0
  54. package/src/lib/runtime-graph-model.d.ts +7 -0
  55. package/src/lib/runtime-graph-model.js.map +1 -1
  56. package/src/lib/runtime-graph.js +3 -0
  57. package/src/lib/runtime-graph.js.map +1 -1
  58. package/src/lib/runtime-html-page.d.ts +7 -0
  59. package/src/lib/runtime-html-page.js +29 -0
  60. package/src/lib/runtime-html-page.js.map +1 -1
  61. package/src/lib/runtime-visualizer.client.d.ts +7 -0
  62. package/src/lib/runtime-visualizer.client.js +32 -0
  63. package/src/lib/runtime-visualizer.client.js.map +1 -1
  64. package/src/lib/runtime-visualizer.js +3 -1
  65. package/src/lib/runtime-visualizer.js.map +1 -1
  66. package/src/lib/saved-snapshot.js +6 -2
  67. package/src/lib/saved-snapshot.js.map +1 -1
  68. package/src/lib/tag-truth.d.ts +20 -3
  69. package/src/lib/tag-truth.js +48 -7
  70. package/src/lib/tag-truth.js.map +1 -1
  71. package/src/nx-policy-registry.js +1 -0
  72. package/src/nx-policy-registry.js.map +1 -1
  73. package/src/rule-help.js +1 -0
  74. package/src/rule-help.js.map +1 -1
  75. package/src/rule-settings.js +12 -1
  76. package/src/rule-settings.js.map +1 -1
@@ -21,16 +21,26 @@
21
21
  * from cache, unchanged); everything else is grey. Which box is which is only known
22
22
  * per checkout, so every box carries all four variants and the page picks one from the
23
23
  * sidecar (graph-impact.ts). The state is never written on the box: color + legend.
24
+ * The words are the Filter's: changed · dependent · dependency (built, unchanged) ·
25
+ * not in this build.
26
+ * - PRODUCT "which products it belongs to" (#1179, graph-products.ts). One product is a solid
27
+ * fill in that product's palette color; two or more (but not all) are the same
28
+ * vertical stripes the multi-runtime box uses, one per product; a box shared by EVERY
29
+ * product is one neutral fill (otherwise a shared foundation would be four-stripe
30
+ * noise); a box in no product is white with a dashed border. The Filter HIDES other
31
+ * products' boxes; this mode keeps every box and makes ownership visible.
24
32
  */
25
33
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.NodeModeStyler = exports.LegendFacts = exports.NodeModeDots = exports.NodeFacts = exports.IMPACT_UNTOUCHED = exports.IMPACT_BUILD_INPUT_BORDER = exports.IMPACT_BUILD_INPUT = exports.IMPACT_AFFECTED = exports.IMPACT_TOUCHED = exports.ROLE_STYLES = exports.RoleStyle = exports.STRIPED_CLASS = exports.NO_RUNTIME_COLOR = exports.FRAMEWORK_ORDER = exports.BASE_ORDER = exports.FRAMEWORK_STYLES = exports.FrameworkStyle = exports.LIGHT_TEXT = exports.DARK_TEXT = exports.GRAPH_MODES = exports.ModeInfo = exports.GraphMode = void 0;
34
+ exports.NodeModeStyler = exports.LegendFacts = exports.ProductCount = exports.NodeModeDots = exports.NodeFacts = exports.IMPACT_UNTOUCHED = exports.IMPACT_BUILD_INPUT_BORDER = exports.IMPACT_BUILD_INPUT = exports.IMPACT_AFFECTED = exports.IMPACT_TOUCHED = exports.ROLE_STYLES = exports.RoleStyle = exports.STRIPED_CLASS = exports.NO_RUNTIME_COLOR = exports.FRAMEWORK_ORDER = exports.BASE_ORDER = exports.FRAMEWORK_STYLES = exports.FrameworkStyle = exports.LIGHT_TEXT = exports.DARK_TEXT = exports.GRAPH_MODES = exports.ModeInfo = exports.GraphMode = void 0;
27
35
  const rules_sdk_1 = require("@webpieces/rules-sdk");
28
36
  const dot_syntax_1 = require("./dot-syntax");
37
+ const graph_products_1 = require("./graph-products");
29
38
  var GraphMode;
30
39
  (function (GraphMode) {
31
40
  GraphMode["RUNTIME"] = "runtime";
32
41
  GraphMode["ARCHITECTURE"] = "architecture";
33
42
  GraphMode["IMPACT"] = "impact";
43
+ GraphMode["PRODUCT"] = "product";
34
44
  })(GraphMode || (exports.GraphMode = GraphMode = {}));
35
45
  /** One selectable mode as the drawer's Color-by pulldown shows it. */
36
46
  class ModeInfo {
@@ -44,11 +54,12 @@ class ModeInfo {
44
54
  }
45
55
  }
46
56
  exports.ModeInfo = ModeInfo;
47
- /** In key order: `1` selects the first, `2` the second, `3` the third. */
57
+ /** In key order: `1` selects the first, `2` the second, `3` the third, `4` the fourth. */
48
58
  exports.GRAPH_MODES = [
49
59
  new ModeInfo(GraphMode.RUNTIME, 'Runtime', 'where the code can run'),
50
60
  new ModeInfo(GraphMode.ARCHITECTURE, 'Architecture', 'servers · clients · APIs'),
51
61
  new ModeInfo(GraphMode.IMPACT, 'Impact', 'what changed'),
62
+ new ModeInfo(GraphMode.PRODUCT, 'Product', 'which products it belongs to'),
52
63
  ];
53
64
  /** Text on a light fill. */
54
65
  exports.DARK_TEXT = '#1a1c22';
@@ -153,12 +164,16 @@ class NodeFacts {
153
164
  level;
154
165
  role;
155
166
  frameworks;
156
- constructor(nodeId, shortName, level, role, frameworks) {
167
+ products;
168
+ constructor(nodeId, shortName, level, role, frameworks,
169
+ /** The products the project belongs to (dependencies.json `products`), sorted; empty for none. */
170
+ products) {
157
171
  this.nodeId = nodeId;
158
172
  this.shortName = shortName;
159
173
  this.level = level;
160
174
  this.role = role;
161
175
  this.frameworks = frameworks;
176
+ this.products = products;
162
177
  }
163
178
  }
164
179
  exports.NodeFacts = NodeFacts;
@@ -170,22 +185,46 @@ class NodeModeDots {
170
185
  affected;
171
186
  buildInput;
172
187
  untouched;
173
- constructor(runtime, architecture, touched, affected, buildInput, untouched) {
188
+ product;
189
+ constructor(runtime, architecture, touched, affected, buildInput, untouched, product) {
174
190
  this.runtime = runtime;
175
191
  this.architecture = architecture;
176
192
  this.touched = touched;
177
193
  this.affected = affected;
178
194
  this.buildInput = buildInput;
179
195
  this.untouched = untouched;
196
+ this.product = product;
180
197
  }
181
198
  }
182
199
  exports.NodeModeDots = NodeModeDots;
200
+ /** How many drawn boxes one product holds, and how many of them no other product does. */
201
+ class ProductCount {
202
+ product;
203
+ total;
204
+ only;
205
+ constructor(product, total, only) {
206
+ this.product = product;
207
+ this.total = total;
208
+ this.only = only;
209
+ }
210
+ }
211
+ exports.ProductCount = ProductCount;
183
212
  /** Which colors and roles the drawn projects actually use, so the legend lists exactly those. */
184
213
  class LegendFacts {
185
214
  frameworks = [];
186
215
  roles = [];
187
216
  multiRuntime = false;
188
217
  noRuntime = false;
218
+ /** One count per declared product, in palette (sorted-name) order; empty when none is declared. */
219
+ products = [];
220
+ /** Boxes shared by every product (two or more declared). */
221
+ sharedByAll = 0;
222
+ /** Boxes in no product. */
223
+ noProduct = 0;
224
+ /** Some box belongs to two or more products, but not to every one: it is striped. */
225
+ multiProduct = false;
226
+ /** Some product is past the palette's eight and is painted with the neutral fill. */
227
+ productOverflow = false;
189
228
  }
190
229
  exports.LegendFacts = LegendFacts;
191
230
  class NodeModeStyler {
@@ -223,11 +262,12 @@ class NodeModeStyler {
223
262
  }
224
263
  return undefined;
225
264
  }
226
- dots(facts) {
227
- return new NodeModeDots(this.runtime(facts), this.plain(facts, this.roleStyle(facts.role).fill, this.roleStyle(facts.role).fontColor, ''), this.plain(facts, exports.IMPACT_TOUCHED, exports.DARK_TEXT, ''), this.plain(facts, exports.IMPACT_AFFECTED, exports.DARK_TEXT, `, color="${exports.IMPACT_TOUCHED}", penwidth=2`), this.plain(facts, exports.IMPACT_BUILD_INPUT, exports.DARK_TEXT, `, color="${exports.IMPACT_BUILD_INPUT_BORDER}", penwidth=1.5`, 'filled,dashed'), this.plain(facts, exports.IMPACT_UNTOUCHED, IMPACT_UNTOUCHED_TEXT, ', color="#b4b9c4"'));
265
+ dots(facts, palette) {
266
+ return new NodeModeDots(this.runtime(facts), this.plain(facts, this.roleStyle(facts.role).fill, this.roleStyle(facts.role).fontColor, ''), this.plain(facts, exports.IMPACT_TOUCHED, exports.DARK_TEXT, ''), this.plain(facts, exports.IMPACT_AFFECTED, exports.DARK_TEXT, `, color="${exports.IMPACT_TOUCHED}", penwidth=2`), this.plain(facts, exports.IMPACT_BUILD_INPUT, exports.DARK_TEXT, `, color="${exports.IMPACT_BUILD_INPUT_BORDER}", penwidth=1.5`, 'filled,dashed'), this.plain(facts, exports.IMPACT_UNTOUCHED, IMPACT_UNTOUCHED_TEXT, ', color="#b4b9c4"'), this.product(facts, palette));
228
267
  }
229
268
  /** Fold one drawn box into the legend facts. */
230
- record(legend, facts) {
269
+ record(legend, facts, palette) {
270
+ this.recordProducts(legend, facts, palette);
231
271
  for (const framework of facts.frameworks) {
232
272
  if (this.styleOf(framework) !== undefined && !legend.frameworks.includes(framework))
233
273
  legend.frameworks.push(framework);
@@ -241,6 +281,26 @@ class NodeModeStyler {
241
281
  if (bases.length === 0)
242
282
  legend.noRuntime = true;
243
283
  }
284
+ /** The Product section's counts: per product (total, and only-this-product), shared-by-all, none. */
285
+ recordProducts(legend, facts, palette) {
286
+ if (legend.products.length === 0 && palette.colors.length > 0) {
287
+ legend.products = palette.names().map((product) => new ProductCount(product, 0, 0));
288
+ legend.productOverflow = palette.overflows();
289
+ }
290
+ if (facts.products.length === 0)
291
+ legend.noProduct++;
292
+ if (palette.sharedByAll(facts.products))
293
+ legend.sharedByAll++;
294
+ else if (facts.products.length > 1)
295
+ legend.multiProduct = true;
296
+ for (const count of legend.products) {
297
+ if (!facts.products.includes(count.product))
298
+ continue;
299
+ count.total++;
300
+ if (facts.products.length === 1)
301
+ count.only++;
302
+ }
303
+ }
244
304
  /**
245
305
  * Line 3 of every box: every framework tag, bases before specializations, any tag outside the
246
306
  * shared vocabulary last (it still runs somewhere somebody declared).
@@ -263,7 +323,13 @@ class NodeModeStyler {
263
323
  }
264
324
  /** `filled` for one runtime; equal `striped` weights for several (the last takes the remainder). */
265
325
  stripeAttrs(bases) {
266
- const colors = bases.map((base) => exports.FRAMEWORK_STYLES[base].color);
326
+ return this.stripes(bases.map((base) => exports.FRAMEWORK_STYLES[base].color));
327
+ }
328
+ /**
329
+ * One equal vertical stripe per color — the renderer the multi-runtime box and the multi-product
330
+ * box share. A single color is a plain fill.
331
+ */
332
+ stripes(colors) {
267
333
  if (colors.length === 1)
268
334
  return `style="filled", fillcolor="${colors[0]}"`;
269
335
  const share = (1 / colors.length).toFixed(3);
@@ -277,6 +343,20 @@ class NodeModeStyler {
277
343
  plain(facts, fill, fontColor, extra, style = 'filled') {
278
344
  return this.statement(facts, `style="${style}", fillcolor="${fill}", fontcolor="${fontColor}"${extra}`, this.labelText(facts, fontColor));
279
345
  }
346
+ /**
347
+ * Product mode (#1179): one product solid in its color; several (not all) striped, one stripe per
348
+ * product; every product the neutral shared fill; no product a white box with a dashed border.
349
+ */
350
+ product(facts, palette) {
351
+ if (facts.products.length === 0)
352
+ return this.plain(facts, graph_products_1.PRODUCT_NONE_FILL, exports.DARK_TEXT, `, color="${graph_products_1.PRODUCT_NONE_BORDER}", penwidth=1.5`, 'filled,dashed');
353
+ if (palette.sharedByAll(facts.products))
354
+ return this.plain(facts, graph_products_1.PRODUCT_SHARED_FILL, exports.DARK_TEXT, '');
355
+ const colors = facts.products.map((product) => palette.colorOf(product));
356
+ if (colors.length === 1)
357
+ return this.plain(facts, colors[0].color, colors[0].fontColor, '');
358
+ return this.statement(facts, `${this.stripes(colors.map((color) => color.color))}, fontcolor="${exports.DARK_TEXT}"`, this.labelText(facts, exports.DARK_TEXT));
359
+ }
280
360
  /**
281
361
  * Runtime mode. No runtime, or one runtime with no specialization: a solid fill. One runtime with a
282
362
  * specialization: NESTED. Several runtimes: full-height stripes with the text across them (a
@@ -1 +1 @@
1
- {"version":3,"file":"graph-color-modes.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-color-modes.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Graph color modes (#1155, restyled by #1158)\n *\n * The architecture graph answers ONE question at a time instead of asking every box to encode two\n * (fill = framework AND border = role). Each box is emitted once per mode, so the page can switch\n * modes and re-render without regenerating anything. Every mode writes the SAME three lines on a box\n * (level + name, role, every framework tag); only the color changes:\n *\n * - RUNTIME \"where the code can run\". One runtime is a solid fill. One runtime plus a\n * specialization (angular/react inside browser, express inside node) is NESTED: an\n * outer frame in the base color around an inner rounded box in the specialization's\n * color, carrying the text. Several runtimes are full-height vertical stripes in the\n * fixed order browser | node | react-native, with the text written across them. The\n * dependency rule becomes visual: a box may depend on a box carrying every color it has.\n * - ARCHITECTURE \"servers · clients · APIs\". A solid fill by ROLE; framework color is not shown.\n * - IMPACT \"what changed\" — on this branch, or in the last commit (graph-impact.ts).\n * Projects owning a changed file are solid amber (\"touched\"); the rest of nx's affected set is light amber (\"affected\": their ci\n * re-runs); every transitive dependency of the affected set that is not itself\n * affected is light slate with a dashed border (\"build input\": compiled or restored\n * from cache, unchanged); everything else is grey. Which box is which is only known\n * per checkout, so every box carries all four variants and the page picks one from the\n * sidecar (graph-impact.ts). The state is never written on the box: color + legend.\n */\n\nimport { KNOWN_FRAMEWORKS, KnownFramework } from '@webpieces/rules-sdk';\nimport { dotValue, htmlLabelText } from './dot-syntax';\n\nexport enum GraphMode {\n RUNTIME = 'runtime',\n ARCHITECTURE = 'architecture',\n IMPACT = 'impact',\n}\n\n/** One selectable mode as the drawer's Color-by pulldown shows it. */\nexport class ModeInfo {\n constructor(\n public readonly mode: GraphMode,\n public readonly name: string,\n public readonly subtitle: string,\n ) {}\n}\n\n/** In key order: `1` selects the first, `2` the second, `3` the third. */\nexport const GRAPH_MODES: readonly ModeInfo[] = [\n new ModeInfo(GraphMode.RUNTIME, 'Runtime', 'where the code can run'),\n new ModeInfo(GraphMode.ARCHITECTURE, 'Architecture', 'servers · clients · APIs'),\n new ModeInfo(GraphMode.IMPACT, 'Impact', 'what changed'),\n];\n\n/** Text on a light fill. */\nexport const DARK_TEXT = '#1a1c22';\n/** Text on a dark fill. */\nexport const LIGHT_TEXT = '#ffffff';\n\n/**\n * A framework value's color, the text color that reads on it, and the BASE runtime it is drawn\n * inside (itself, for a base).\n */\nexport class FrameworkStyle {\n constructor(\n public readonly framework: KnownFramework,\n public readonly base: KnownFramework,\n public readonly color: string,\n public readonly fontColor: string,\n public readonly description: string,\n ) {}\n\n isBase(): boolean {\n return this.framework === this.base;\n }\n}\n\n/**\n * Keyed by the shared vocabulary, so a framework added to KNOWN_FRAMEWORKS without a color here is a\n * COMPILE error — not a box silently painted grey and missing from the legend, which is exactly how\n * react-native shipped (#1155). Every base is mid-light, so dark text reads on every stripe.\n */\nexport const FRAMEWORK_STYLES: Record<KnownFramework, FrameworkStyle> = {\n browser: new FrameworkStyle('browser', 'browser', '#7d8cf2', DARK_TEXT, 'browser base runtime'),\n node: new FrameworkStyle('node', 'node', '#6fcf8a', DARK_TEXT, 'node base runtime'),\n 'react-native': new FrameworkStyle(\n 'react-native',\n 'react-native',\n '#34b8a6',\n DARK_TEXT,\n 'its own runtime, not a browser',\n ),\n angular: new FrameworkStyle('angular', 'browser', '#c8243f', LIGHT_TEXT, 'inside browser'),\n react: new FrameworkStyle('react', 'browser', '#7fd8ff', DARK_TEXT, 'inside browser'),\n express: new FrameworkStyle('express', 'node', '#2e7d4f', LIGHT_TEXT, 'inside node'),\n};\n\n/** Stripe order, left to right. Fixed, so the same set always reads the same way. */\nexport const BASE_ORDER: readonly KnownFramework[] = ['browser', 'node', 'react-native'];\n\n/**\n * The Filter popover's Runtime chips, and the order line 3 of a box lists framework tags in: the\n * bases first, then the specializations.\n */\nexport const FRAMEWORK_ORDER: readonly KnownFramework[] = [\n 'browser',\n 'node',\n 'react-native',\n 'angular',\n 'react',\n 'express',\n];\n\n/** A box with no known framework tag. */\nexport const NO_RUNTIME_COLOR = '#e4e6ec';\n\n/** The class Graphviz stamps on a multi-runtime box's `<g>`: its outline is its LAST shape. */\nexport const STRIPED_CLASS = 'wp-striped';\n\n/** Margin of base color around a specialization's inner box. */\nconst NEST_PAD = 5;\n/** Padding between the inner box's edge and its text. */\nconst INNER_PAD = 6;\n\n/** A role's solid fill and the text color that stays readable on it. */\nexport class RoleStyle {\n constructor(\n public readonly role: string,\n public readonly fill: string,\n public readonly fontColor: string,\n public readonly description: string,\n ) {}\n}\n\n/**\n * Every role role-resolver knows, in legend order (runnable things first) — also the Filter popover's\n * Role chips. `bundle` is indigo so no box color equals the page's violet accent.\n */\nexport const ROLE_STYLES: readonly RoleStyle[] = [\n new RoleStyle('server', '#4caf50', LIGHT_TEXT, 'runnable server'),\n new RoleStyle('client', '#e5533d', LIGHT_TEXT, 'browser or mobile client app'),\n new RoleStyle('app', '#1e88e5', LIGHT_TEXT, 'runnable non-HTTP app'),\n new RoleStyle('bundle', '#4b44c8', LIGHT_TEXT, 'bundles several apps'),\n new RoleStyle('api-lib', '#f08a24', DARK_TEXT, 'contract + DTOs'),\n new RoleStyle('api-client', '#1a9aa6', LIGHT_TEXT, 'contract + SDK implementation'),\n new RoleStyle('designed-lib', '#7d8597', LIGHT_TEXT, 'library with a generated design'),\n new RoleStyle('lib', '#d7dae2', DARK_TEXT, 'plain library'),\n];\n\n/** The four impact shades (graph-impact.ts decides which box gets which). */\nexport const IMPACT_TOUCHED = '#f5a524';\nexport const IMPACT_AFFECTED = '#fde3b0';\nexport const IMPACT_BUILD_INPUT = '#e3e9f2';\nexport const IMPACT_BUILD_INPUT_BORDER = '#7c8aa3';\nexport const IMPACT_UNTOUCHED = '#eef0f4';\nconst IMPACT_UNTOUCHED_TEXT = '#6b7180';\n\n/** What a box's per-mode rendering — and the page's Filter — is computed from. */\nexport class NodeFacts {\n constructor(\n public readonly nodeId: string,\n public readonly shortName: string,\n public readonly level: number,\n public readonly role: string,\n public readonly frameworks: string[],\n ) {}\n}\n\n/** One box's DOT statement for every mode (and for each impact state). */\nexport class NodeModeDots {\n constructor(\n public readonly runtime: string,\n public readonly architecture: string,\n public readonly touched: string,\n public readonly affected: string,\n public readonly buildInput: string,\n public readonly untouched: string,\n ) {}\n}\n\n/** Which colors and roles the drawn projects actually use, so the legend lists exactly those. */\nexport class LegendFacts {\n frameworks: string[] = [];\n roles: string[] = [];\n multiRuntime = false;\n noRuntime = false;\n}\n\nexport class NodeModeStyler {\n styleOf(framework: string): FrameworkStyle | undefined {\n if (!(KNOWN_FRAMEWORKS as readonly string[]).includes(framework)) return undefined;\n return FRAMEWORK_STYLES[framework as KnownFramework];\n }\n\n roleStyle(role: string): RoleStyle {\n const style = ROLE_STYLES.find((candidate: RoleStyle): boolean => candidate.role === role);\n return style ?? (ROLE_STYLES.find((s: RoleStyle): boolean => s.role === 'lib') as RoleStyle);\n }\n\n /** Every role the renderer styles; a spec asserts this covers role-resolver's KNOWN_ROLES. */\n styledRoles(): string[] {\n return ROLE_STYLES.map((style: RoleStyle): string => style.role);\n }\n\n /** The base runtimes of a framework set, in stripe order (a specialization implies its base). */\n bases(frameworks: string[]): KnownFramework[] {\n const present = new Set<KnownFramework>();\n for (const framework of frameworks) {\n const style = this.styleOf(framework);\n if (style !== undefined) present.add(style.base);\n }\n return BASE_ORDER.filter((base: KnownFramework): boolean => present.has(base));\n }\n\n /** The specialization drawn inside `base`, if the set carries one. */\n specialization(base: KnownFramework, frameworks: string[]): FrameworkStyle | undefined {\n for (const framework of KNOWN_FRAMEWORKS) {\n const style = FRAMEWORK_STYLES[framework];\n if (style.isBase() || style.base !== base) continue;\n if (frameworks.includes(framework)) return style;\n }\n return undefined;\n }\n\n dots(facts: NodeFacts): NodeModeDots {\n return new NodeModeDots(\n this.runtime(facts),\n this.plain(facts, this.roleStyle(facts.role).fill, this.roleStyle(facts.role).fontColor, ''),\n this.plain(facts, IMPACT_TOUCHED, DARK_TEXT, ''),\n this.plain(facts, IMPACT_AFFECTED, DARK_TEXT, `, color=\"${IMPACT_TOUCHED}\", penwidth=2`),\n this.plain(\n facts,\n IMPACT_BUILD_INPUT,\n DARK_TEXT,\n `, color=\"${IMPACT_BUILD_INPUT_BORDER}\", penwidth=1.5`,\n 'filled,dashed',\n ),\n this.plain(facts, IMPACT_UNTOUCHED, IMPACT_UNTOUCHED_TEXT, ', color=\"#b4b9c4\"'),\n );\n }\n\n /** Fold one drawn box into the legend facts. */\n record(legend: LegendFacts, facts: NodeFacts): void {\n for (const framework of facts.frameworks) {\n if (this.styleOf(framework) !== undefined && !legend.frameworks.includes(framework))\n legend.frameworks.push(framework);\n }\n const role = this.roleStyle(facts.role).role;\n if (!legend.roles.includes(role)) legend.roles.push(role);\n const bases = this.bases(facts.frameworks);\n if (bases.length > 1) legend.multiRuntime = true;\n if (bases.length === 0) legend.noRuntime = true;\n }\n\n /**\n * Line 3 of every box: every framework tag, bases before specializations, any tag outside the\n * shared vocabulary last (it still runs somewhere somebody declared).\n */\n frameworkLine(frameworks: string[]): string {\n if (frameworks.length === 0) return 'no framework tag';\n const known = FRAMEWORK_ORDER.filter((framework: KnownFramework): boolean =>\n frameworks.includes(framework),\n );\n const unknown = frameworks.filter((framework: string): boolean => this.styleOf(framework) === undefined);\n return [...known, ...unknown].join(' · ');\n }\n\n /**\n * The three lines, identical in every mode: `L3 name` (level dimmed, name bold), the role, and\n * the framework tags. The level is dimmed by giving its text color an alpha channel, which\n * Graphviz writes as `fill-opacity`, so it dims on any fill.\n */\n labelText(facts: NodeFacts, fontColor: string): string {\n return (\n `<FONT COLOR=\"${fontColor}99\">L${facts.level}</FONT>&#160;&#160;<B>${htmlLabelText(facts.shortName)}</B>` +\n `<BR/>${htmlLabelText(facts.role)}<BR/>${htmlLabelText(this.frameworkLine(facts.frameworks))}`\n );\n }\n\n /** `filled` for one runtime; equal `striped` weights for several (the last takes the remainder). */\n stripeAttrs(bases: KnownFramework[]): string {\n const colors = bases.map((base: KnownFramework): string => FRAMEWORK_STYLES[base].color);\n if (colors.length === 1) return `style=\"filled\", fillcolor=\"${colors[0]}\"`;\n const share = (1 / colors.length).toFixed(3);\n const weighted = colors.map((color: string, index: number): string =>\n index === colors.length - 1 ? color : `${color};${share}`,\n );\n return `style=\"striped\", fillcolor=\"${weighted.join(':')}\", class=\"${STRIPED_CLASS}\"`;\n }\n\n private statement(facts: NodeFacts, attrs: string, label: string): string {\n return ` \"${dotValue(facts.nodeId)}\" [${attrs}, label=<${label}>];\\n`;\n }\n\n /** A solid fill, the three lines on it. `extra` carries border attributes. */\n private plain(facts: NodeFacts, fill: string, fontColor: string, extra: string, style = 'filled'): string {\n return this.statement(\n facts,\n `style=\"${style}\", fillcolor=\"${fill}\", fontcolor=\"${fontColor}\"${extra}`,\n this.labelText(facts, fontColor),\n );\n }\n\n /**\n * Runtime mode. No runtime, or one runtime with no specialization: a solid fill. One runtime with a\n * specialization: NESTED. Several runtimes: full-height stripes with the text across them (a\n * specialization on such a box shows in line 3 only).\n */\n private runtime(facts: NodeFacts): string {\n const bases = this.bases(facts.frameworks);\n if (bases.length === 0) return this.plain(facts, NO_RUNTIME_COLOR, DARK_TEXT, '');\n if (bases.length > 1)\n return this.statement(\n facts,\n `${this.stripeAttrs(bases)}, fontcolor=\"${DARK_TEXT}\"`,\n this.labelText(facts, DARK_TEXT),\n );\n const base = FRAMEWORK_STYLES[bases[0]];\n const inner = this.specialization(bases[0], facts.frameworks);\n if (inner === undefined) return this.plain(facts, base.color, base.fontColor, '');\n return this.statement(\n facts,\n `style=\"filled\", fillcolor=\"${base.color}\", fontcolor=\"${inner.fontColor}\", margin=0`,\n this.nestedTable(base.color, inner, this.labelText(facts, inner.fontColor)),\n );\n }\n\n /**\n * An outer cell in the base color whose padding is the visible frame, holding an inner ROUNDED\n * table in the specialization's color that carries the text. The node's own polygon stays its\n * FIRST shape, so the hover glow and the lock outline land on the outer box only.\n */\n private nestedTable(base: string, inner: FrameworkStyle, text: string): string {\n return (\n '<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">' +\n `<TR><TD BGCOLOR=\"${base}\" CELLPADDING=\"${NEST_PAD}\">` +\n `<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"${INNER_PAD}\" BGCOLOR=\"${inner.color}\" STYLE=\"rounded\">` +\n `<TR><TD>${text}</TD></TR></TABLE></TD></TR></TABLE>`\n );\n }\n}\n"]}
1
+ {"version":3,"file":"graph-color-modes.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-color-modes.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Graph color modes (#1155, restyled by #1158)\n *\n * The architecture graph answers ONE question at a time instead of asking every box to encode two\n * (fill = framework AND border = role). Each box is emitted once per mode, so the page can switch\n * modes and re-render without regenerating anything. Every mode writes the SAME three lines on a box\n * (level + name, role, every framework tag); only the color changes:\n *\n * - RUNTIME \"where the code can run\". One runtime is a solid fill. One runtime plus a\n * specialization (angular/react inside browser, express inside node) is NESTED: an\n * outer frame in the base color around an inner rounded box in the specialization's\n * color, carrying the text. Several runtimes are full-height vertical stripes in the\n * fixed order browser | node | react-native, with the text written across them. The\n * dependency rule becomes visual: a box may depend on a box carrying every color it has.\n * - ARCHITECTURE \"servers · clients · APIs\". A solid fill by ROLE; framework color is not shown.\n * - IMPACT \"what changed\" — on this branch, or in the last commit (graph-impact.ts).\n * Projects owning a changed file are solid amber (\"touched\"); the rest of nx's affected set is light amber (\"affected\": their ci\n * re-runs); every transitive dependency of the affected set that is not itself\n * affected is light slate with a dashed border (\"build input\": compiled or restored\n * from cache, unchanged); everything else is grey. Which box is which is only known\n * per checkout, so every box carries all four variants and the page picks one from the\n * sidecar (graph-impact.ts). The state is never written on the box: color + legend.\n * The words are the Filter's: changed · dependent · dependency (built, unchanged) ·\n * not in this build.\n * - PRODUCT \"which products it belongs to\" (#1179, graph-products.ts). One product is a solid\n * fill in that product's palette color; two or more (but not all) are the same\n * vertical stripes the multi-runtime box uses, one per product; a box shared by EVERY\n * product is one neutral fill (otherwise a shared foundation would be four-stripe\n * noise); a box in no product is white with a dashed border. The Filter HIDES other\n * products' boxes; this mode keeps every box and makes ownership visible.\n */\n\nimport { KNOWN_FRAMEWORKS, KnownFramework } from '@webpieces/rules-sdk';\nimport { dotValue, htmlLabelText } from './dot-syntax';\nimport { PRODUCT_NONE_BORDER, PRODUCT_NONE_FILL, PRODUCT_SHARED_FILL, ProductColor, ProductPalette } from './graph-products';\n\nexport enum GraphMode {\n RUNTIME = 'runtime',\n ARCHITECTURE = 'architecture',\n IMPACT = 'impact',\n PRODUCT = 'product',\n}\n\n/** One selectable mode as the drawer's Color-by pulldown shows it. */\nexport class ModeInfo {\n constructor(\n public readonly mode: GraphMode,\n public readonly name: string,\n public readonly subtitle: string,\n ) {}\n}\n\n/** In key order: `1` selects the first, `2` the second, `3` the third, `4` the fourth. */\nexport const GRAPH_MODES: readonly ModeInfo[] = [\n new ModeInfo(GraphMode.RUNTIME, 'Runtime', 'where the code can run'),\n new ModeInfo(GraphMode.ARCHITECTURE, 'Architecture', 'servers · clients · APIs'),\n new ModeInfo(GraphMode.IMPACT, 'Impact', 'what changed'),\n new ModeInfo(GraphMode.PRODUCT, 'Product', 'which products it belongs to'),\n];\n\n/** Text on a light fill. */\nexport const DARK_TEXT = '#1a1c22';\n/** Text on a dark fill. */\nexport const LIGHT_TEXT = '#ffffff';\n\n/**\n * A framework value's color, the text color that reads on it, and the BASE runtime it is drawn\n * inside (itself, for a base).\n */\nexport class FrameworkStyle {\n constructor(\n public readonly framework: KnownFramework,\n public readonly base: KnownFramework,\n public readonly color: string,\n public readonly fontColor: string,\n public readonly description: string,\n ) {}\n\n isBase(): boolean {\n return this.framework === this.base;\n }\n}\n\n/**\n * Keyed by the shared vocabulary, so a framework added to KNOWN_FRAMEWORKS without a color here is a\n * COMPILE error — not a box silently painted grey and missing from the legend, which is exactly how\n * react-native shipped (#1155). Every base is mid-light, so dark text reads on every stripe.\n */\nexport const FRAMEWORK_STYLES: Record<KnownFramework, FrameworkStyle> = {\n browser: new FrameworkStyle('browser', 'browser', '#7d8cf2', DARK_TEXT, 'browser base runtime'),\n node: new FrameworkStyle('node', 'node', '#6fcf8a', DARK_TEXT, 'node base runtime'),\n 'react-native': new FrameworkStyle(\n 'react-native',\n 'react-native',\n '#34b8a6',\n DARK_TEXT,\n 'its own runtime, not a browser',\n ),\n angular: new FrameworkStyle('angular', 'browser', '#c8243f', LIGHT_TEXT, 'inside browser'),\n react: new FrameworkStyle('react', 'browser', '#7fd8ff', DARK_TEXT, 'inside browser'),\n express: new FrameworkStyle('express', 'node', '#2e7d4f', LIGHT_TEXT, 'inside node'),\n};\n\n/** Stripe order, left to right. Fixed, so the same set always reads the same way. */\nexport const BASE_ORDER: readonly KnownFramework[] = ['browser', 'node', 'react-native'];\n\n/**\n * The Filter popover's Runtime chips, and the order line 3 of a box lists framework tags in: the\n * bases first, then the specializations.\n */\nexport const FRAMEWORK_ORDER: readonly KnownFramework[] = [\n 'browser',\n 'node',\n 'react-native',\n 'angular',\n 'react',\n 'express',\n];\n\n/** A box with no known framework tag. */\nexport const NO_RUNTIME_COLOR = '#e4e6ec';\n\n/** The class Graphviz stamps on a multi-runtime box's `<g>`: its outline is its LAST shape. */\nexport const STRIPED_CLASS = 'wp-striped';\n\n/** Margin of base color around a specialization's inner box. */\nconst NEST_PAD = 5;\n/** Padding between the inner box's edge and its text. */\nconst INNER_PAD = 6;\n\n/** A role's solid fill and the text color that stays readable on it. */\nexport class RoleStyle {\n constructor(\n public readonly role: string,\n public readonly fill: string,\n public readonly fontColor: string,\n public readonly description: string,\n ) {}\n}\n\n/**\n * Every role role-resolver knows, in legend order (runnable things first) — also the Filter popover's\n * Role chips. `bundle` is indigo so no box color equals the page's violet accent.\n */\nexport const ROLE_STYLES: readonly RoleStyle[] = [\n new RoleStyle('server', '#4caf50', LIGHT_TEXT, 'runnable server'),\n new RoleStyle('client', '#e5533d', LIGHT_TEXT, 'browser or mobile client app'),\n new RoleStyle('app', '#1e88e5', LIGHT_TEXT, 'runnable non-HTTP app'),\n new RoleStyle('bundle', '#4b44c8', LIGHT_TEXT, 'bundles several apps'),\n new RoleStyle('api-lib', '#f08a24', DARK_TEXT, 'contract + DTOs'),\n new RoleStyle('api-client', '#1a9aa6', LIGHT_TEXT, 'contract + SDK implementation'),\n new RoleStyle('designed-lib', '#7d8597', LIGHT_TEXT, 'library with a generated design'),\n new RoleStyle('lib', '#d7dae2', DARK_TEXT, 'plain library'),\n];\n\n/** The four impact shades (graph-impact.ts decides which box gets which). */\nexport const IMPACT_TOUCHED = '#f5a524';\nexport const IMPACT_AFFECTED = '#fde3b0';\nexport const IMPACT_BUILD_INPUT = '#e3e9f2';\nexport const IMPACT_BUILD_INPUT_BORDER = '#7c8aa3';\nexport const IMPACT_UNTOUCHED = '#eef0f4';\nconst IMPACT_UNTOUCHED_TEXT = '#6b7180';\n\n/** What a box's per-mode rendering — and the page's Filter — is computed from. */\nexport class NodeFacts {\n constructor(\n public readonly nodeId: string,\n public readonly shortName: string,\n public readonly level: number,\n public readonly role: string,\n public readonly frameworks: string[],\n /** The products the project belongs to (dependencies.json `products`), sorted; empty for none. */\n public readonly products: string[],\n ) {}\n}\n\n/** One box's DOT statement for every mode (and for each impact state). */\nexport class NodeModeDots {\n constructor(\n public readonly runtime: string,\n public readonly architecture: string,\n public readonly touched: string,\n public readonly affected: string,\n public readonly buildInput: string,\n public readonly untouched: string,\n public readonly product: string,\n ) {}\n}\n\n/** How many drawn boxes one product holds, and how many of them no other product does. */\nexport class ProductCount {\n constructor(\n public readonly product: string,\n public total: number,\n public only: number,\n ) {}\n}\n\n/** Which colors and roles the drawn projects actually use, so the legend lists exactly those. */\nexport class LegendFacts {\n frameworks: string[] = [];\n roles: string[] = [];\n multiRuntime = false;\n noRuntime = false;\n /** One count per declared product, in palette (sorted-name) order; empty when none is declared. */\n products: ProductCount[] = [];\n /** Boxes shared by every product (two or more declared). */\n sharedByAll = 0;\n /** Boxes in no product. */\n noProduct = 0;\n /** Some box belongs to two or more products, but not to every one: it is striped. */\n multiProduct = false;\n /** Some product is past the palette's eight and is painted with the neutral fill. */\n productOverflow = false;\n}\n\nexport class NodeModeStyler {\n styleOf(framework: string): FrameworkStyle | undefined {\n if (!(KNOWN_FRAMEWORKS as readonly string[]).includes(framework)) return undefined;\n return FRAMEWORK_STYLES[framework as KnownFramework];\n }\n\n roleStyle(role: string): RoleStyle {\n const style = ROLE_STYLES.find((candidate: RoleStyle): boolean => candidate.role === role);\n return style ?? (ROLE_STYLES.find((s: RoleStyle): boolean => s.role === 'lib') as RoleStyle);\n }\n\n /** Every role the renderer styles; a spec asserts this covers role-resolver's KNOWN_ROLES. */\n styledRoles(): string[] {\n return ROLE_STYLES.map((style: RoleStyle): string => style.role);\n }\n\n /** The base runtimes of a framework set, in stripe order (a specialization implies its base). */\n bases(frameworks: string[]): KnownFramework[] {\n const present = new Set<KnownFramework>();\n for (const framework of frameworks) {\n const style = this.styleOf(framework);\n if (style !== undefined) present.add(style.base);\n }\n return BASE_ORDER.filter((base: KnownFramework): boolean => present.has(base));\n }\n\n /** The specialization drawn inside `base`, if the set carries one. */\n specialization(base: KnownFramework, frameworks: string[]): FrameworkStyle | undefined {\n for (const framework of KNOWN_FRAMEWORKS) {\n const style = FRAMEWORK_STYLES[framework];\n if (style.isBase() || style.base !== base) continue;\n if (frameworks.includes(framework)) return style;\n }\n return undefined;\n }\n\n dots(facts: NodeFacts, palette: ProductPalette): NodeModeDots {\n return new NodeModeDots(\n this.runtime(facts),\n this.plain(facts, this.roleStyle(facts.role).fill, this.roleStyle(facts.role).fontColor, ''),\n this.plain(facts, IMPACT_TOUCHED, DARK_TEXT, ''),\n this.plain(facts, IMPACT_AFFECTED, DARK_TEXT, `, color=\"${IMPACT_TOUCHED}\", penwidth=2`),\n this.plain(\n facts,\n IMPACT_BUILD_INPUT,\n DARK_TEXT,\n `, color=\"${IMPACT_BUILD_INPUT_BORDER}\", penwidth=1.5`,\n 'filled,dashed',\n ),\n this.plain(facts, IMPACT_UNTOUCHED, IMPACT_UNTOUCHED_TEXT, ', color=\"#b4b9c4\"'),\n this.product(facts, palette),\n );\n }\n\n /** Fold one drawn box into the legend facts. */\n record(legend: LegendFacts, facts: NodeFacts, palette: ProductPalette): void {\n this.recordProducts(legend, facts, palette);\n for (const framework of facts.frameworks) {\n if (this.styleOf(framework) !== undefined && !legend.frameworks.includes(framework))\n legend.frameworks.push(framework);\n }\n const role = this.roleStyle(facts.role).role;\n if (!legend.roles.includes(role)) legend.roles.push(role);\n const bases = this.bases(facts.frameworks);\n if (bases.length > 1) legend.multiRuntime = true;\n if (bases.length === 0) legend.noRuntime = true;\n }\n\n /** The Product section's counts: per product (total, and only-this-product), shared-by-all, none. */\n private recordProducts(legend: LegendFacts, facts: NodeFacts, palette: ProductPalette): void {\n if (legend.products.length === 0 && palette.colors.length > 0) {\n legend.products = palette.names().map((product: string): ProductCount => new ProductCount(product, 0, 0));\n legend.productOverflow = palette.overflows();\n }\n if (facts.products.length === 0) legend.noProduct++;\n if (palette.sharedByAll(facts.products)) legend.sharedByAll++;\n else if (facts.products.length > 1) legend.multiProduct = true;\n for (const count of legend.products) {\n if (!facts.products.includes(count.product)) continue;\n count.total++;\n if (facts.products.length === 1) count.only++;\n }\n }\n\n /**\n * Line 3 of every box: every framework tag, bases before specializations, any tag outside the\n * shared vocabulary last (it still runs somewhere somebody declared).\n */\n frameworkLine(frameworks: string[]): string {\n if (frameworks.length === 0) return 'no framework tag';\n const known = FRAMEWORK_ORDER.filter((framework: KnownFramework): boolean =>\n frameworks.includes(framework),\n );\n const unknown = frameworks.filter((framework: string): boolean => this.styleOf(framework) === undefined);\n return [...known, ...unknown].join(' · ');\n }\n\n /**\n * The three lines, identical in every mode: `L3 name` (level dimmed, name bold), the role, and\n * the framework tags. The level is dimmed by giving its text color an alpha channel, which\n * Graphviz writes as `fill-opacity`, so it dims on any fill.\n */\n labelText(facts: NodeFacts, fontColor: string): string {\n return (\n `<FONT COLOR=\"${fontColor}99\">L${facts.level}</FONT>&#160;&#160;<B>${htmlLabelText(facts.shortName)}</B>` +\n `<BR/>${htmlLabelText(facts.role)}<BR/>${htmlLabelText(this.frameworkLine(facts.frameworks))}`\n );\n }\n\n /** `filled` for one runtime; equal `striped` weights for several (the last takes the remainder). */\n stripeAttrs(bases: KnownFramework[]): string {\n return this.stripes(bases.map((base: KnownFramework): string => FRAMEWORK_STYLES[base].color));\n }\n\n /**\n * One equal vertical stripe per color — the renderer the multi-runtime box and the multi-product\n * box share. A single color is a plain fill.\n */\n stripes(colors: string[]): string {\n if (colors.length === 1) return `style=\"filled\", fillcolor=\"${colors[0]}\"`;\n const share = (1 / colors.length).toFixed(3);\n const weighted = colors.map((color: string, index: number): string =>\n index === colors.length - 1 ? color : `${color};${share}`,\n );\n return `style=\"striped\", fillcolor=\"${weighted.join(':')}\", class=\"${STRIPED_CLASS}\"`;\n }\n\n private statement(facts: NodeFacts, attrs: string, label: string): string {\n return ` \"${dotValue(facts.nodeId)}\" [${attrs}, label=<${label}>];\\n`;\n }\n\n /** A solid fill, the three lines on it. `extra` carries border attributes. */\n private plain(facts: NodeFacts, fill: string, fontColor: string, extra: string, style = 'filled'): string {\n return this.statement(\n facts,\n `style=\"${style}\", fillcolor=\"${fill}\", fontcolor=\"${fontColor}\"${extra}`,\n this.labelText(facts, fontColor),\n );\n }\n\n /**\n * Product mode (#1179): one product solid in its color; several (not all) striped, one stripe per\n * product; every product the neutral shared fill; no product a white box with a dashed border.\n */\n private product(facts: NodeFacts, palette: ProductPalette): string {\n if (facts.products.length === 0)\n return this.plain(facts, PRODUCT_NONE_FILL, DARK_TEXT, `, color=\"${PRODUCT_NONE_BORDER}\", penwidth=1.5`, 'filled,dashed');\n if (palette.sharedByAll(facts.products)) return this.plain(facts, PRODUCT_SHARED_FILL, DARK_TEXT, '');\n const colors = facts.products.map((product: string): ProductColor => palette.colorOf(product));\n if (colors.length === 1) return this.plain(facts, colors[0].color, colors[0].fontColor, '');\n return this.statement(\n facts,\n `${this.stripes(colors.map((color: ProductColor): string => color.color))}, fontcolor=\"${DARK_TEXT}\"`,\n this.labelText(facts, DARK_TEXT),\n );\n }\n\n /**\n * Runtime mode. No runtime, or one runtime with no specialization: a solid fill. One runtime with a\n * specialization: NESTED. Several runtimes: full-height stripes with the text across them (a\n * specialization on such a box shows in line 3 only).\n */\n private runtime(facts: NodeFacts): string {\n const bases = this.bases(facts.frameworks);\n if (bases.length === 0) return this.plain(facts, NO_RUNTIME_COLOR, DARK_TEXT, '');\n if (bases.length > 1)\n return this.statement(\n facts,\n `${this.stripeAttrs(bases)}, fontcolor=\"${DARK_TEXT}\"`,\n this.labelText(facts, DARK_TEXT),\n );\n const base = FRAMEWORK_STYLES[bases[0]];\n const inner = this.specialization(bases[0], facts.frameworks);\n if (inner === undefined) return this.plain(facts, base.color, base.fontColor, '');\n return this.statement(\n facts,\n `style=\"filled\", fillcolor=\"${base.color}\", fontcolor=\"${inner.fontColor}\", margin=0`,\n this.nestedTable(base.color, inner, this.labelText(facts, inner.fontColor)),\n );\n }\n\n /**\n * An outer cell in the base color whose padding is the visible frame, holding an inner ROUNDED\n * table in the specialization's color that carries the text. The node's own polygon stays its\n * FIRST shape, so the hover glow and the lock outline land on the outer box only.\n */\n private nestedTable(base: string, inner: FrameworkStyle, text: string): string {\n return (\n '<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">' +\n `<TR><TD BGCOLOR=\"${base}\" CELLPADDING=\"${NEST_PAD}\">` +\n `<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"${INNER_PAD}\" BGCOLOR=\"${inner.color}\" STYLE=\"rounded\">` +\n `<TR><TD>${text}</TD></TR></TABLE></TD></TR></TABLE>`\n );\n }\n}\n"]}
@@ -13,6 +13,8 @@ exports.compareGraphs = compareGraphs;
13
13
  const METADATA_FIELDS = [
14
14
  'framework',
15
15
  'webpiecesRuntime',
16
+ // #1179: a product gaining or losing a project is an architecture change validate must report.
17
+ 'products',
16
18
  'shortDescription',
17
19
  'responsibilitiesFile',
18
20
  'designFile',
@@ -1 +1 @@
1
- {"version":3,"file":"graph-comparator.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-comparator.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Graph Comparator\n *\n * Compares the current generated graph with the saved (blessed) graph.\n * Used in validate mode to ensure developers have updated the graph file.\n */\n\nimport type { EnhancedGraph, GraphEntry } from './graph-sorter';\n\n/**\n * A changed metadata field on a project (framework, shortDescription, ...)\n */\nexport interface FieldChange {\n field: string;\n from: string | undefined;\n to: string | undefined;\n}\n\n/**\n * Difference between two graphs\n */\nexport interface GraphDiff {\n added: string[];\n removed: string[];\n modified: {\n project: string;\n addedDeps: string[];\n removedDeps: string[];\n levelChanged: { from: number; to: number } | null;\n changedFields: FieldChange[];\n }[];\n}\n\n/**\n * Metadata fields compared per project (beyond level + dependsOn)\n */\nconst METADATA_FIELDS: ReadonlyArray<keyof GraphEntry & string> = [\n 'framework',\n 'webpiecesRuntime',\n 'shortDescription',\n 'responsibilitiesFile',\n 'designFile',\n 'runtimeDeclaration',\n];\n\n/**\n * Comparison result\n */\nexport interface ComparisonResult {\n identical: boolean;\n diff: GraphDiff;\n summary: string;\n}\n\n/**\n * Compare two graphs and return the differences\n *\n * @param current - Currently generated graph\n * @param saved - Previously saved (blessed) graph\n * @returns Comparison result with detailed diff\n */\nexport function compareGraphs(current: EnhancedGraph, saved: EnhancedGraph): ComparisonResult {\n const currentProjects = new Set(Object.keys(current));\n const savedProjects = new Set(Object.keys(saved));\n\n const diff: GraphDiff = {\n added: [],\n removed: [],\n modified: [],\n };\n\n // Find added projects\n for (const project of currentProjects) {\n if (!savedProjects.has(project)) {\n diff.added.push(project);\n }\n }\n\n // Find removed projects\n for (const project of savedProjects) {\n if (!currentProjects.has(project)) {\n diff.removed.push(project);\n }\n }\n\n // Find modified projects\n findModifiedProjects(current, saved, currentProjects, savedProjects, diff);\n\n const identical =\n diff.added.length === 0 && diff.removed.length === 0 && diff.modified.length === 0;\n\n const summary = identical ? 'Graphs are identical' : buildSummary(diff);\n\n return {\n identical,\n diff,\n summary,\n };\n}\n\nfunction findModifiedProjects(\n current: EnhancedGraph,\n saved: EnhancedGraph,\n currentProjects: Set<string>,\n savedProjects: Set<string>,\n diff: GraphDiff,\n): void {\n for (const project of currentProjects) {\n if (!savedProjects.has(project)) continue;\n\n const currentEntry = current[project];\n const savedEntry = saved[project];\n\n const currentDeps = new Set(currentEntry.dependsOn);\n const savedDeps = new Set(savedEntry.dependsOn);\n\n const addedDeps: string[] = [];\n const removedDeps: string[] = [];\n\n for (const dep of currentDeps) {\n if (!savedDeps.has(dep)) {\n addedDeps.push(dep);\n }\n }\n\n for (const dep of savedDeps) {\n if (!currentDeps.has(dep)) {\n removedDeps.push(dep);\n }\n }\n\n const levelChanged =\n currentEntry.level !== savedEntry.level\n ? { from: savedEntry.level, to: currentEntry.level }\n : null;\n\n const changedFields = findChangedFields(currentEntry, savedEntry);\n\n if (\n addedDeps.length > 0 ||\n removedDeps.length > 0 ||\n levelChanged ||\n changedFields.length > 0\n ) {\n diff.modified.push({\n project,\n addedDeps,\n removedDeps,\n levelChanged,\n changedFields,\n });\n }\n }\n}\n\n/**\n * Normalize a metadata field value to a comparable/displayable string. The\n * `framework` field is a string[] env set (compared by value, joined for\n * display); every other field is already a plain string.\n */\nfunction normalizeFieldValue(value: string | string[] | undefined): string | undefined {\n if (value === undefined) return undefined;\n return Array.isArray(value) ? value.join(', ') : value;\n}\n\nfunction findChangedFields(currentEntry: GraphEntry, savedEntry: GraphEntry): FieldChange[] {\n const changes: FieldChange[] = [];\n for (const field of METADATA_FIELDS) {\n const from = normalizeFieldValue(savedEntry[field] as string | string[] | undefined);\n const to = normalizeFieldValue(currentEntry[field] as string | string[] | undefined);\n if (from !== to) {\n changes.push({ field, from, to });\n }\n }\n if (currentEntry.runtimeComposition !== savedEntry.runtimeComposition)\n changes.push({\n field: 'runtimeComposition',\n from: String(savedEntry.runtimeComposition),\n to: String(currentEntry.runtimeComposition),\n });\n // apiRelations is a nested object — compare by canonical JSON. Both sides are\n // built with sorted owners + refs (scanner) / preserved key order (loader), so\n // string equality is a faithful deep-equality here.\n const fromRelations = JSON.stringify(savedEntry.apiRelations ?? {});\n const toRelations = JSON.stringify(currentEntry.apiRelations ?? {});\n if (fromRelations !== toRelations) {\n changes.push({ field: 'apiRelations', from: fromRelations, to: toRelations });\n }\n return changes;\n}\n\nfunction formatFieldValue(value: string | undefined): string {\n if (value === undefined) return '(none)';\n const MAX_SUMMARY_VALUE_CHARS = 60;\n return value.length > MAX_SUMMARY_VALUE_CHARS\n ? `\"${value.slice(0, MAX_SUMMARY_VALUE_CHARS)}...\"`\n : `\"${value}\"`;\n}\n\nfunction buildSummary(diff: GraphDiff): string {\n const summaryParts: string[] = [];\n\n if (diff.added.length > 0) {\n summaryParts.push(`Added projects: ${diff.added.join(', ')}`);\n }\n\n if (diff.removed.length > 0) {\n summaryParts.push(`Removed projects: ${diff.removed.join(', ')}`);\n }\n\n for (const mod of diff.modified) {\n const parts: string[] = [];\n if (mod.addedDeps.length > 0) {\n parts.push(`+deps: ${mod.addedDeps.join(', ')}`);\n }\n if (mod.removedDeps.length > 0) {\n parts.push(`-deps: ${mod.removedDeps.join(', ')}`);\n }\n if (mod.levelChanged) {\n parts.push(`level: ${mod.levelChanged.from} -> ${mod.levelChanged.to}`);\n }\n for (const change of mod.changedFields) {\n parts.push(\n `${change.field}: ${formatFieldValue(change.from)} -> ${formatFieldValue(change.to)}`,\n );\n }\n if (parts.length > 0) {\n summaryParts.push(`${mod.project}: ${parts.join('; ')}`);\n }\n }\n\n return summaryParts.join('\\n');\n}\n"]}
1
+ {"version":3,"file":"graph-comparator.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-comparator.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Graph Comparator\n *\n * Compares the current generated graph with the saved (blessed) graph.\n * Used in validate mode to ensure developers have updated the graph file.\n */\n\nimport type { EnhancedGraph, GraphEntry } from './graph-sorter';\n\n/**\n * A changed metadata field on a project (framework, shortDescription, ...)\n */\nexport interface FieldChange {\n field: string;\n from: string | undefined;\n to: string | undefined;\n}\n\n/**\n * Difference between two graphs\n */\nexport interface GraphDiff {\n added: string[];\n removed: string[];\n modified: {\n project: string;\n addedDeps: string[];\n removedDeps: string[];\n levelChanged: { from: number; to: number } | null;\n changedFields: FieldChange[];\n }[];\n}\n\n/**\n * Metadata fields compared per project (beyond level + dependsOn)\n */\nconst METADATA_FIELDS: ReadonlyArray<keyof GraphEntry & string> = [\n 'framework',\n 'webpiecesRuntime',\n // #1179: a product gaining or losing a project is an architecture change validate must report.\n 'products',\n 'shortDescription',\n 'responsibilitiesFile',\n 'designFile',\n 'runtimeDeclaration',\n];\n\n/**\n * Comparison result\n */\nexport interface ComparisonResult {\n identical: boolean;\n diff: GraphDiff;\n summary: string;\n}\n\n/**\n * Compare two graphs and return the differences\n *\n * @param current - Currently generated graph\n * @param saved - Previously saved (blessed) graph\n * @returns Comparison result with detailed diff\n */\nexport function compareGraphs(current: EnhancedGraph, saved: EnhancedGraph): ComparisonResult {\n const currentProjects = new Set(Object.keys(current));\n const savedProjects = new Set(Object.keys(saved));\n\n const diff: GraphDiff = {\n added: [],\n removed: [],\n modified: [],\n };\n\n // Find added projects\n for (const project of currentProjects) {\n if (!savedProjects.has(project)) {\n diff.added.push(project);\n }\n }\n\n // Find removed projects\n for (const project of savedProjects) {\n if (!currentProjects.has(project)) {\n diff.removed.push(project);\n }\n }\n\n // Find modified projects\n findModifiedProjects(current, saved, currentProjects, savedProjects, diff);\n\n const identical =\n diff.added.length === 0 && diff.removed.length === 0 && diff.modified.length === 0;\n\n const summary = identical ? 'Graphs are identical' : buildSummary(diff);\n\n return {\n identical,\n diff,\n summary,\n };\n}\n\nfunction findModifiedProjects(\n current: EnhancedGraph,\n saved: EnhancedGraph,\n currentProjects: Set<string>,\n savedProjects: Set<string>,\n diff: GraphDiff,\n): void {\n for (const project of currentProjects) {\n if (!savedProjects.has(project)) continue;\n\n const currentEntry = current[project];\n const savedEntry = saved[project];\n\n const currentDeps = new Set(currentEntry.dependsOn);\n const savedDeps = new Set(savedEntry.dependsOn);\n\n const addedDeps: string[] = [];\n const removedDeps: string[] = [];\n\n for (const dep of currentDeps) {\n if (!savedDeps.has(dep)) {\n addedDeps.push(dep);\n }\n }\n\n for (const dep of savedDeps) {\n if (!currentDeps.has(dep)) {\n removedDeps.push(dep);\n }\n }\n\n const levelChanged =\n currentEntry.level !== savedEntry.level\n ? { from: savedEntry.level, to: currentEntry.level }\n : null;\n\n const changedFields = findChangedFields(currentEntry, savedEntry);\n\n if (\n addedDeps.length > 0 ||\n removedDeps.length > 0 ||\n levelChanged ||\n changedFields.length > 0\n ) {\n diff.modified.push({\n project,\n addedDeps,\n removedDeps,\n levelChanged,\n changedFields,\n });\n }\n }\n}\n\n/**\n * Normalize a metadata field value to a comparable/displayable string. The\n * `framework` field is a string[] env set (compared by value, joined for\n * display); every other field is already a plain string.\n */\nfunction normalizeFieldValue(value: string | string[] | undefined): string | undefined {\n if (value === undefined) return undefined;\n return Array.isArray(value) ? value.join(', ') : value;\n}\n\nfunction findChangedFields(currentEntry: GraphEntry, savedEntry: GraphEntry): FieldChange[] {\n const changes: FieldChange[] = [];\n for (const field of METADATA_FIELDS) {\n const from = normalizeFieldValue(savedEntry[field] as string | string[] | undefined);\n const to = normalizeFieldValue(currentEntry[field] as string | string[] | undefined);\n if (from !== to) {\n changes.push({ field, from, to });\n }\n }\n if (currentEntry.runtimeComposition !== savedEntry.runtimeComposition)\n changes.push({\n field: 'runtimeComposition',\n from: String(savedEntry.runtimeComposition),\n to: String(currentEntry.runtimeComposition),\n });\n // apiRelations is a nested object — compare by canonical JSON. Both sides are\n // built with sorted owners + refs (scanner) / preserved key order (loader), so\n // string equality is a faithful deep-equality here.\n const fromRelations = JSON.stringify(savedEntry.apiRelations ?? {});\n const toRelations = JSON.stringify(currentEntry.apiRelations ?? {});\n if (fromRelations !== toRelations) {\n changes.push({ field: 'apiRelations', from: fromRelations, to: toRelations });\n }\n return changes;\n}\n\nfunction formatFieldValue(value: string | undefined): string {\n if (value === undefined) return '(none)';\n const MAX_SUMMARY_VALUE_CHARS = 60;\n return value.length > MAX_SUMMARY_VALUE_CHARS\n ? `\"${value.slice(0, MAX_SUMMARY_VALUE_CHARS)}...\"`\n : `\"${value}\"`;\n}\n\nfunction buildSummary(diff: GraphDiff): string {\n const summaryParts: string[] = [];\n\n if (diff.added.length > 0) {\n summaryParts.push(`Added projects: ${diff.added.join(', ')}`);\n }\n\n if (diff.removed.length > 0) {\n summaryParts.push(`Removed projects: ${diff.removed.join(', ')}`);\n }\n\n for (const mod of diff.modified) {\n const parts: string[] = [];\n if (mod.addedDeps.length > 0) {\n parts.push(`+deps: ${mod.addedDeps.join(', ')}`);\n }\n if (mod.removedDeps.length > 0) {\n parts.push(`-deps: ${mod.removedDeps.join(', ')}`);\n }\n if (mod.levelChanged) {\n parts.push(`level: ${mod.levelChanged.from} -> ${mod.levelChanged.to}`);\n }\n for (const change of mod.changedFields) {\n parts.push(\n `${change.field}: ${formatFieldValue(change.from)} -> ${formatFieldValue(change.to)}`,\n );\n }\n if (parts.length > 0) {\n summaryParts.push(`${mod.project}: ${parts.join('; ')}`);\n }\n }\n\n return summaryParts.join('\\n');\n}\n"]}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Entry points always sit in the top row (#1179).
3
+ *
4
+ * A project's level is its dependency depth, so before this a client could sit at L6 while a server
5
+ * sat at L7, and finding the runnable top-level projects meant scanning several rows. After it, the
6
+ * TOP row of the architecture graph is exactly the servers, clients and apps — and nothing else.
7
+ *
8
+ * The rule, applied once roles are resolved (enrichGraph):
9
+ *
10
+ * top = max( highest level of any entry point, 1 + highest level of any other non-bundle project )
11
+ *
12
+ * The second term guarantees no library ever shares the top row. Every entry point gets `top`. Then
13
+ * anything that depends on an entry point is re-levelled bottom-up as `1 + max(level of its deps)` —
14
+ * `role-dependency` only lets a `role:bundle` do that, so in a valid graph only bundles move, and they
15
+ * stay above the apps they aggregate.
16
+ *
17
+ * The one entry point that can depend on another is a server orchestrator booting other servers
18
+ * (`role-dependency` allows server → server). It cannot share the row of a server it depends on, so it
19
+ * sits one row higher: `max(top, 1 + max(level of its deps))`. Every dependency therefore still sits
20
+ * STRICTLY below its dependent — the generate executor re-asserts that after promotion.
21
+ *
22
+ * Entry points only ever move UP. The runtime graph is NOT changed: its levels are call depth
23
+ * (client → server → fsdb server), and pinning there would put an fsdb server beside its caller.
24
+ */
25
+ import type { EnhancedGraph } from './graph-sorter';
26
+ export declare const BUNDLE_ROLE = "bundle";
27
+ export declare class EntryPointLevels {
28
+ /** The roles that are entry points — graph-metadata.ts's APP_ROLES (server, app, client). */
29
+ private readonly appRoles;
30
+ constructor(
31
+ /** The roles that are entry points — graph-metadata.ts's APP_ROLES (server, app, client). */
32
+ appRoles: readonly string[]);
33
+ /** Re-level `graph` in place. A graph with no entry point is left exactly as it was. */
34
+ promote(graph: EnhancedGraph): void;
35
+ }
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ /**
3
+ * Entry points always sit in the top row (#1179).
4
+ *
5
+ * A project's level is its dependency depth, so before this a client could sit at L6 while a server
6
+ * sat at L7, and finding the runnable top-level projects meant scanning several rows. After it, the
7
+ * TOP row of the architecture graph is exactly the servers, clients and apps — and nothing else.
8
+ *
9
+ * The rule, applied once roles are resolved (enrichGraph):
10
+ *
11
+ * top = max( highest level of any entry point, 1 + highest level of any other non-bundle project )
12
+ *
13
+ * The second term guarantees no library ever shares the top row. Every entry point gets `top`. Then
14
+ * anything that depends on an entry point is re-levelled bottom-up as `1 + max(level of its deps)` —
15
+ * `role-dependency` only lets a `role:bundle` do that, so in a valid graph only bundles move, and they
16
+ * stay above the apps they aggregate.
17
+ *
18
+ * The one entry point that can depend on another is a server orchestrator booting other servers
19
+ * (`role-dependency` allows server → server). It cannot share the row of a server it depends on, so it
20
+ * sits one row higher: `max(top, 1 + max(level of its deps))`. Every dependency therefore still sits
21
+ * STRICTLY below its dependent — the generate executor re-asserts that after promotion.
22
+ *
23
+ * Entry points only ever move UP. The runtime graph is NOT changed: its levels are call depth
24
+ * (client → server → fsdb server), and pinning there would put an fsdb server beside its caller.
25
+ */
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.EntryPointLevels = exports.BUNDLE_ROLE = void 0;
28
+ exports.BUNDLE_ROLE = 'bundle';
29
+ class EntryPointLevels {
30
+ appRoles;
31
+ constructor(
32
+ /** The roles that are entry points — graph-metadata.ts's APP_ROLES (server, app, client). */
33
+ appRoles) {
34
+ this.appRoles = appRoles;
35
+ }
36
+ /** Re-level `graph` in place. A graph with no entry point is left exactly as it was. */
37
+ promote(graph) {
38
+ const projects = Object.keys(graph);
39
+ const isApp = (project) => this.appRoles.includes(graph[project].role ?? '');
40
+ const apps = projects.filter(isApp);
41
+ if (apps.length === 0)
42
+ return;
43
+ let top = Math.max(...apps.map((project) => graph[project].level));
44
+ for (const project of projects) {
45
+ if (isApp(project) || graph[project].role === exports.BUNDLE_ROLE)
46
+ continue;
47
+ top = Math.max(top, graph[project].level + 1);
48
+ }
49
+ // Ascending ORIGINAL level: every dependency sits strictly lower, so it is final before any
50
+ // project that depends on it is visited.
51
+ const order = [...projects].sort((a, b) => graph[a].level - graph[b].level || a.localeCompare(b));
52
+ for (const project of order) {
53
+ const entry = graph[project];
54
+ const above = 1 + Math.max(-1, ...entry.dependsOn.map((dep) => graph[dep]?.level ?? -1));
55
+ entry.level = isApp(project) ? Math.max(top, above) : Math.max(entry.level, above);
56
+ }
57
+ }
58
+ }
59
+ exports.EntryPointLevels = EntryPointLevels;
60
+ //# sourceMappingURL=graph-entry-levels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-entry-levels.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-entry-levels.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;;;AAIU,QAAA,WAAW,GAAG,QAAQ,CAAC;AAEpC,MAAa,gBAAgB;IAGJ;IAFrB;IACI,6FAA6F;IAC5E,QAA2B;QAA3B,aAAQ,GAAR,QAAQ,CAAmB;IAC7C,CAAC;IAEJ,wFAAwF;IACxF,OAAO,CAAC,KAAoB;QACxB,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,KAAK,GAAG,CAAC,OAAe,EAAW,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QAC9F,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAE9B,IAAI,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,OAAe,EAAU,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QACnF,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC7B,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK,mBAAW;gBAAE,SAAS;YACpE,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAClD,CAAC;QAED,4FAA4F;QAC5F,yCAAyC;QACzC,MAAM,KAAK,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAC5B,CAAC,CAAS,EAAE,CAAS,EAAU,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAC1F,CAAC;QACF,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;YAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;YAC7B,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAW,EAAU,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACzG,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACvF,CAAC;IACL,CAAC;CACJ;AA9BD,4CA8BC","sourcesContent":["/**\n * Entry points always sit in the top row (#1179).\n *\n * A project's level is its dependency depth, so before this a client could sit at L6 while a server\n * sat at L7, and finding the runnable top-level projects meant scanning several rows. After it, the\n * TOP row of the architecture graph is exactly the servers, clients and apps — and nothing else.\n *\n * The rule, applied once roles are resolved (enrichGraph):\n *\n * top = max( highest level of any entry point, 1 + highest level of any other non-bundle project )\n *\n * The second term guarantees no library ever shares the top row. Every entry point gets `top`. Then\n * anything that depends on an entry point is re-levelled bottom-up as `1 + max(level of its deps)` —\n * `role-dependency` only lets a `role:bundle` do that, so in a valid graph only bundles move, and they\n * stay above the apps they aggregate.\n *\n * The one entry point that can depend on another is a server orchestrator booting other servers\n * (`role-dependency` allows server → server). It cannot share the row of a server it depends on, so it\n * sits one row higher: `max(top, 1 + max(level of its deps))`. Every dependency therefore still sits\n * STRICTLY below its dependent — the generate executor re-asserts that after promotion.\n *\n * Entry points only ever move UP. The runtime graph is NOT changed: its levels are call depth\n * (client → server → fsdb server), and pinning there would put an fsdb server beside its caller.\n */\n\nimport type { EnhancedGraph } from './graph-sorter';\n\nexport const BUNDLE_ROLE = 'bundle';\n\nexport class EntryPointLevels {\n constructor(\n /** The roles that are entry points — graph-metadata.ts's APP_ROLES (server, app, client). */\n private readonly appRoles: readonly string[],\n ) {}\n\n /** Re-level `graph` in place. A graph with no entry point is left exactly as it was. */\n promote(graph: EnhancedGraph): void {\n const projects = Object.keys(graph);\n const isApp = (project: string): boolean => this.appRoles.includes(graph[project].role ?? '');\n const apps = projects.filter(isApp);\n if (apps.length === 0) return;\n\n let top = Math.max(...apps.map((project: string): number => graph[project].level));\n for (const project of projects) {\n if (isApp(project) || graph[project].role === BUNDLE_ROLE) continue;\n top = Math.max(top, graph[project].level + 1);\n }\n\n // Ascending ORIGINAL level: every dependency sits strictly lower, so it is final before any\n // project that depends on it is visited.\n const order = [...projects].sort(\n (a: string, b: string): number => graph[a].level - graph[b].level || a.localeCompare(b),\n );\n for (const project of order) {\n const entry = graph[project];\n const above = 1 + Math.max(-1, ...entry.dependsOn.map((dep: string): number => graph[dep]?.level ?? -1));\n entry.level = isApp(project) ? Math.max(top, above) : Math.max(entry.level, above);\n }\n }\n}\n"]}
@@ -98,8 +98,8 @@ export declare class ImpactReport {
98
98
  readonly changedFiles: number;
99
99
  /**
100
100
  * Transitive dependencies of the TOUCHED set that are not touched themselves (node ids): the
101
- * Filter popover's "Changed + what they use". ("Changed + what uses them" is touched +
102
- * affected, which nx already computed as the dependents whose tests re-run.)
101
+ * Filter popover's "Changed + dependencies". ("Changed + dependents" is touched + affected,
102
+ * which nx already computed as the dependents whose tests re-run.)
103
103
  */
104
104
  readonly dependencies: string[];
105
105
  /**
@@ -130,8 +130,8 @@ export declare class ImpactReport {
130
130
  buildInputs: string[], changedFiles: number,
131
131
  /**
132
132
  * Transitive dependencies of the TOUCHED set that are not touched themselves (node ids): the
133
- * Filter popover's "Changed + what they use". ("Changed + what uses them" is touched +
134
- * affected, which nx already computed as the dependents whose tests re-run.)
133
+ * Filter popover's "Changed + dependencies". ("Changed + dependents" is touched + affected,
134
+ * which nx already computed as the dependents whose tests re-run.)
135
135
  */
136
136
  dependencies: string[],
137
137
  /**
@@ -245,7 +245,7 @@ export declare class NxImpactScanner {
245
245
  /**
246
246
  * Every transitive dependency of `roots` that is not itself one of them: a pure downward walk over
247
247
  * the graph's own dependsOn edges, no nx call. Of the affected set these are the build inputs; of
248
- * the touched set, "what they use".
248
+ * the touched set, "Changed + dependencies".
249
249
  */
250
250
  buildInputs(roots: string[], graph: EnhancedGraph): string[];
251
251
  /** Affected projects that own one of the changed files, by nx's own ownership. */
@@ -122,8 +122,8 @@ class ImpactReport {
122
122
  buildInputs, changedFiles,
123
123
  /**
124
124
  * Transitive dependencies of the TOUCHED set that are not touched themselves (node ids): the
125
- * Filter popover's "Changed + what they use". ("Changed + what uses them" is touched +
126
- * affected, which nx already computed as the dependents whose tests re-run.)
125
+ * Filter popover's "Changed + dependencies". ("Changed + dependents" is touched + affected,
126
+ * which nx already computed as the dependents whose tests re-run.)
127
127
  */
128
128
  dependencies,
129
129
  /**
@@ -429,7 +429,7 @@ class NxImpactScanner {
429
429
  /**
430
430
  * Every transitive dependency of `roots` that is not itself one of them: a pure downward walk over
431
431
  * the graph's own dependsOn edges, no nx call. Of the affected set these are the build inputs; of
432
- * the touched set, "what they use".
432
+ * the touched set, "Changed + dependencies".
433
433
  */
434
434
  buildInputs(roots, graph) {
435
435
  const inSet = new Set(roots);
@@ -570,8 +570,8 @@ class ImpactRefresh {
570
570
  return `ℹ️ Impact · ${result.label}: unavailable — ${result.reason}`;
571
571
  if (result.reason !== '')
572
572
  return `✅ Impact · ${result.label}: ${result.reason}`;
573
- return (`✅ Impact · ${result.label}: ${result.touched.length} touched · ${result.affected.length} affected · ` +
574
- `${result.buildInputs.length} build inputs, from ${result.changedFiles} changed file(s)`);
573
+ return (`✅ Impact · ${result.label}: ${result.touched.length} changed · ${result.affected.length} dependents · ` +
574
+ `${result.buildInputs.length} dependencies, from ${result.changedFiles} changed file(s)`);
575
575
  }
576
576
  }
577
577
  exports.ImpactRefresh = ImpactRefresh;