@webpieces/nx-webpieces-rules 0.4.970 → 0.4.972

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 (58) hide show
  1. package/README.md +8 -8
  2. package/executors.json +1 -6
  3. package/package.json +7 -7
  4. package/src/executors/help/executor.js +1 -3
  5. package/src/executors/help/executor.js.map +1 -1
  6. package/src/executors/visualize-runtime/executor.d.ts +22 -3
  7. package/src/executors/visualize-runtime/executor.js +55 -8
  8. package/src/executors/visualize-runtime/executor.js.map +1 -1
  9. package/src/executors/visualize-runtime/schema.json +7 -2
  10. package/src/lib/graph-color-modes.d.ts +10 -0
  11. package/src/lib/graph-color-modes.js +16 -1
  12. package/src/lib/graph-color-modes.js.map +1 -1
  13. package/src/lib/graph-mode-state.client.d.ts +6 -1
  14. package/src/lib/graph-mode-state.client.js +9 -1
  15. package/src/lib/graph-mode-state.client.js.map +1 -1
  16. package/src/lib/graph-page-shell.d.ts +10 -2
  17. package/src/lib/graph-page-shell.js +20 -8
  18. package/src/lib/graph-page-shell.js.map +1 -1
  19. package/src/lib/graph-render-model.d.ts +10 -0
  20. package/src/lib/graph-render-model.js +7 -0
  21. package/src/lib/graph-render-model.js.map +1 -1
  22. package/src/lib/graph-visualizer.client.js +2 -2
  23. package/src/lib/graph-visualizer.client.js.map +1 -1
  24. package/src/lib/runtime-edge-merge.d.ts +18 -0
  25. package/src/lib/runtime-edge-merge.js +38 -0
  26. package/src/lib/runtime-edge-merge.js.map +1 -0
  27. package/src/lib/runtime-goal-comparison.js +1 -1
  28. package/src/lib/runtime-goal-comparison.js.map +1 -1
  29. package/src/lib/runtime-goal-file.d.ts +6 -11
  30. package/src/lib/runtime-goal-file.js +10 -23
  31. package/src/lib/runtime-goal-file.js.map +1 -1
  32. package/src/lib/runtime-goal-validator.d.ts +4 -4
  33. package/src/lib/runtime-goal-validator.js +4 -4
  34. package/src/lib/runtime-goal-validator.js.map +1 -1
  35. package/src/lib/runtime-goal-view.d.ts +18 -11
  36. package/src/lib/runtime-goal-view.js +27 -27
  37. package/src/lib/runtime-goal-view.js.map +1 -1
  38. package/src/lib/runtime-html-page.d.ts +2 -0
  39. package/src/lib/runtime-html-page.js +8 -2
  40. package/src/lib/runtime-html-page.js.map +1 -1
  41. package/src/lib/runtime-union-graph.d.ts +0 -2
  42. package/src/lib/runtime-union-graph.js +3 -19
  43. package/src/lib/runtime-union-graph.js.map +1 -1
  44. package/src/lib/runtime-visualizer.d.ts +2 -3
  45. package/src/lib/runtime-visualizer.js +3 -4
  46. package/src/lib/runtime-visualizer.js.map +1 -1
  47. package/src/lib/runtime-viz-options.d.ts +3 -5
  48. package/src/lib/runtime-viz-options.js +3 -7
  49. package/src/lib/runtime-viz-options.js.map +1 -1
  50. package/src/plugin.js +0 -1
  51. package/src/plugin.js.map +1 -1
  52. package/src/runtime-targets.d.ts +0 -6
  53. package/src/runtime-targets.js +1 -18
  54. package/src/runtime-targets.js.map +1 -1
  55. package/src/executors/visualize-runtime-goal/executor.d.ts +0 -27
  56. package/src/executors/visualize-runtime-goal/executor.js +0 -99
  57. package/src/executors/visualize-runtime-goal/executor.js.map +0 -1
  58. package/src/executors/visualize-runtime-goal/schema.json +0 -13
@@ -1,58 +1,58 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RuntimeComparisonInputs = void 0;
3
+ exports.RuntimeComparisonInputs = exports.MISSING_GOAL_MENU_NOTE = exports.RuntimeComparisonGap = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const path = tslib_1.__importStar(require("path"));
4
6
  const rules_config_1 = require("@webpieces/rules-config");
5
- const runtime_graph_io_1 = require("./runtime-graph-io");
6
- const saved_snapshot_1 = require("./saved-snapshot");
7
7
  const runtime_goal_file_1 = require("./runtime-goal-file");
8
8
  const graph_render_model_1 = require("./graph-render-model");
9
9
  const runtime_goal_comparison_1 = require("./runtime-goal-comparison");
10
10
  const toError_1 = require("../toError");
11
11
  /**
12
- * Loads either saved runtime file for the OTHER page's comparison, turning a missing or unusable file
13
- * into the reason Goal vs Current is unavailable — never into "no differences".
12
+ * Why Goal vs Current cannot be drawn (#1239, #1373): the full `reason` (console, tooltip, legend —
13
+ * with its path and repair) and the short `menuNote` the Color-by menu shows under the dimmed item.
14
+ */
15
+ class RuntimeComparisonGap {
16
+ reason;
17
+ menuNote;
18
+ constructor(reason, menuNote = reason) {
19
+ this.reason = reason;
20
+ this.menuNote = menuNote;
21
+ }
22
+ }
23
+ exports.RuntimeComparisonGap = RuntimeComparisonGap;
24
+ /** The menu line under the dimmed Goal vs Current item when the goal file is absent (#1373). */
25
+ exports.MISSING_GOAL_MENU_NOTE = `${path.posix.basename(runtime_goal_file_1.DEFAULT_RUNTIME_GOAL_PATH)} does not exist`;
26
+ /**
27
+ * Loads the saved goal for the runtime page's comparison, turning a missing or unusable file into
28
+ * the reason Goal vs Current is unavailable — never into "no differences".
14
29
  */
15
30
  class RuntimeComparisonInputs {
16
- snapshot;
17
31
  goalFile;
18
- constructor(snapshot = new saved_snapshot_1.SavedSnapshot(), goalFile = new runtime_goal_file_1.RuntimeGoalFile()) {
19
- this.snapshot = snapshot;
32
+ constructor(goalFile = new runtime_goal_file_1.RuntimeGoalFile()) {
20
33
  this.goalFile = goalFile;
21
34
  }
22
- /** The saved current graph, or why it cannot be compared. */
23
- current(workspaceRoot) {
24
- // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- an unusable comparison input becomes the mode's stated reason; the page itself still renders
25
- try {
26
- return this.snapshot.loadRuntime(workspaceRoot);
27
- }
28
- catch (err) {
29
- const error = (0, toError_1.toError)(err);
30
- return `No usable saved current runtime graph at ${runtime_graph_io_1.DEFAULT_RUNTIME_GRAPH_PATH}: ${this.text(error)}`;
31
- }
32
- }
33
35
  /** The saved goal, or why it cannot be compared (missing: how to start one). */
34
36
  goal(workspaceRoot) {
35
37
  if (!this.goalFile.exists(workspaceRoot))
36
- return `No runtime goal at ${runtime_goal_file_1.DEFAULT_RUNTIME_GOAL_PATH}. Start a proposal with: ${runtime_goal_file_1.RUNTIME_GOAL_INIT_COMMAND}`;
38
+ return new RuntimeComparisonGap(`No runtime goal at ${runtime_goal_file_1.DEFAULT_RUNTIME_GOAL_PATH}. Start a proposal with: ${runtime_goal_file_1.RUNTIME_GOAL_INIT_COMMAND}`, exports.MISSING_GOAL_MENU_NOTE);
37
39
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- an unusable comparison input becomes the mode's stated reason; the page itself still renders
38
40
  try {
39
41
  return this.goalFile.load(workspaceRoot);
40
42
  }
41
43
  catch (err) {
42
44
  const error = (0, toError_1.toError)(err);
43
- return this.text(error);
45
+ return new RuntimeComparisonGap(this.text(error));
44
46
  }
45
47
  }
46
48
  view(current, goal, options) {
47
- if (typeof current === 'string')
48
- return this.unavailable(current);
49
- if (typeof goal === 'string')
49
+ if (goal instanceof RuntimeComparisonGap)
50
50
  return this.unavailable(goal);
51
51
  return new runtime_goal_comparison_1.RuntimeGoalComparison().build(current, goal, options);
52
52
  }
53
- /** Goal vs Current with no overlay: the mode is disabled and says `reason` instead. */
54
- unavailable(reason) {
55
- return new graph_render_model_1.RuntimeComparisonView(false, reason, 'Current → Goal', '', null, {}, {}, []);
53
+ /** Goal vs Current with no overlay: the mode is dimmed and says `gap` instead. */
54
+ unavailable(gap) {
55
+ return new graph_render_model_1.RuntimeComparisonView(false, gap.reason, gap.menuNote, 'Current → Goal', '', null, {}, {}, []);
56
56
  }
57
57
  text(error) {
58
58
  if (error instanceof rules_config_1.RuleFailError)
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-goal-view.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-goal-view.ts"],"names":[],"mappings":";;;AAAA,0DAAgF;AAEhF,yDAAgE;AAChE,qDAAiD;AACjD,2DAA4G;AAC5G,6DAA6D;AAC7D,uEAAkE;AAElE,wCAAqC;AAErC;;;GAGG;AACH,MAAa,uBAAuB;IAEX;IACA;IAFrB,YACqB,WAA0B,IAAI,8BAAa,EAAE,EAC7C,WAA4B,IAAI,mCAAe,EAAE;QADjD,aAAQ,GAAR,QAAQ,CAAqC;QAC7C,aAAQ,GAAR,QAAQ,CAAyC;IACnE,CAAC;IAEJ,6DAA6D;IAC7D,OAAO,CAAC,aAAqB;QACzB,8JAA8J;QAC9J,IAAI,CAAC;YACD,OAAO,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;QACpD,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAA,iBAAO,EAAC,GAAG,CAAC,CAAC;YAC3B,OAAO,4CAA4C,6CAA0B,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACzG,CAAC;IACL,CAAC;IAED,gFAAgF;IAChF,IAAI,CAAC,aAAqB;QACtB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC;YACpC,OAAO,sBAAsB,6CAAyB,4BAA4B,6CAAyB,EAAE,CAAC;QAClH,8JAA8J;QAC9J,IAAI,CAAC;YACD,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC7C,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAA,iBAAO,EAAC,GAAG,CAAC,CAAC;YAC3B,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;IACL,CAAC;IAED,IAAI,CAAC,OAA8B,EAAE,IAA2B,EAAE,OAA0B;QACxF,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAClE,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAC5D,OAAO,IAAI,+CAAqB,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACrE,CAAC;IAED,uFAAuF;IACvF,WAAW,CAAC,MAAc;QACtB,OAAO,IAAI,0CAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IAC5F,CAAC;IAEO,IAAI,CAAC,KAAY;QACrB,IAAI,KAAK,YAAY,4BAAa;YAAE,OAAO,IAAA,qCAAsB,EAAC,KAAK,CAAC,CAAC;QACzE,MAAM,KAAK,CAAC;IAChB,CAAC;CACJ;AA7CD,0DA6CC","sourcesContent":["import { RuleFailError, renderRuleFailForHuman } from '@webpieces/rules-config';\nimport type { RuntimeGraph } from './runtime-graph-model';\nimport { DEFAULT_RUNTIME_GRAPH_PATH } from './runtime-graph-io';\nimport { SavedSnapshot } from './saved-snapshot';\nimport { DEFAULT_RUNTIME_GOAL_PATH, RUNTIME_GOAL_INIT_COMMAND, RuntimeGoalFile } from './runtime-goal-file';\nimport { RuntimeComparisonView } from './graph-render-model';\nimport { RuntimeGoalComparison } from './runtime-goal-comparison';\nimport type { RuntimeVizOptions } from './runtime-viz-options';\nimport { toError } from '../toError';\n\n/**\n * Loads either saved runtime file for the OTHER page's comparison, turning a missing or unusable file\n * into the reason Goal vs Current is unavailable — never into \"no differences\".\n */\nexport class RuntimeComparisonInputs {\n constructor(\n private readonly snapshot: SavedSnapshot = new SavedSnapshot(),\n private readonly goalFile: RuntimeGoalFile = new RuntimeGoalFile(),\n ) {}\n\n /** The saved current graph, or why it cannot be compared. */\n current(workspaceRoot: string): RuntimeGraph | string {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- an unusable comparison input becomes the mode's stated reason; the page itself still renders\n try {\n return this.snapshot.loadRuntime(workspaceRoot);\n } catch (err: unknown) {\n const error = toError(err);\n return `No usable saved current runtime graph at ${DEFAULT_RUNTIME_GRAPH_PATH}: ${this.text(error)}`;\n }\n }\n\n /** The saved goal, or why it cannot be compared (missing: how to start one). */\n goal(workspaceRoot: string): RuntimeGraph | string {\n if (!this.goalFile.exists(workspaceRoot))\n return `No runtime goal at ${DEFAULT_RUNTIME_GOAL_PATH}. Start a proposal with: ${RUNTIME_GOAL_INIT_COMMAND}`;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- an unusable comparison input becomes the mode's stated reason; the page itself still renders\n try {\n return this.goalFile.load(workspaceRoot);\n } catch (err: unknown) {\n const error = toError(err);\n return this.text(error);\n }\n }\n\n view(current: RuntimeGraph | string, goal: RuntimeGraph | string, options: RuntimeVizOptions): RuntimeComparisonView {\n if (typeof current === 'string') return this.unavailable(current);\n if (typeof goal === 'string') return this.unavailable(goal);\n return new RuntimeGoalComparison().build(current, goal, options);\n }\n\n /** Goal vs Current with no overlay: the mode is disabled and says `reason` instead. */\n unavailable(reason: string): RuntimeComparisonView {\n return new RuntimeComparisonView(false, reason, 'Current → Goal', '', null, {}, {}, []);\n }\n\n private text(error: Error): string {\n if (error instanceof RuleFailError) return renderRuleFailForHuman(error);\n throw error;\n }\n}\n"]}
1
+ {"version":3,"file":"runtime-goal-view.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-goal-view.ts"],"names":[],"mappings":";;;;AAAA,mDAA6B;AAC7B,0DAAgF;AAEhF,2DAA4G;AAC5G,6DAA6D;AAC7D,uEAAkE;AAElE,wCAAqC;AAErC;;;GAGG;AACH,MAAa,oBAAoB;IAET;IACA;IAFpB,YACoB,MAAc,EACd,WAAmB,MAAM;QADzB,WAAM,GAAN,MAAM,CAAQ;QACd,aAAQ,GAAR,QAAQ,CAAiB;IAC1C,CAAC;CACP;AALD,oDAKC;AAED,gGAAgG;AACnF,QAAA,sBAAsB,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,6CAAyB,CAAC,iBAAiB,CAAC;AAEzG;;;GAGG;AACH,MAAa,uBAAuB;IACH;IAA7B,YAA6B,WAA4B,IAAI,mCAAe,EAAE;QAAjD,aAAQ,GAAR,QAAQ,CAAyC;IAAG,CAAC;IAElF,gFAAgF;IAChF,IAAI,CAAC,aAAqB;QACtB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC;YACpC,OAAO,IAAI,oBAAoB,CAC3B,sBAAsB,6CAAyB,4BAA4B,6CAAyB,EAAE,EACtG,8BAAsB,CACzB,CAAC;QACN,8JAA8J;QAC9J,IAAI,CAAC;YACD,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC7C,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAA,iBAAO,EAAC,GAAG,CAAC,CAAC;YAC3B,OAAO,IAAI,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QACtD,CAAC;IACL,CAAC;IAED,IAAI,CAAC,OAAqB,EAAE,IAAyC,EAAE,OAA0B;QAC7F,IAAI,IAAI,YAAY,oBAAoB;YAAE,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACxE,OAAO,IAAI,+CAAqB,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACrE,CAAC;IAED,kFAAkF;IAClF,WAAW,CAAC,GAAyB;QACjC,OAAO,IAAI,0CAAqB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,EAAE,gBAAgB,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IAC9G,CAAC;IAEO,IAAI,CAAC,KAAY;QACrB,IAAI,KAAK,YAAY,4BAAa;YAAE,OAAO,IAAA,qCAAsB,EAAC,KAAK,CAAC,CAAC;QACzE,MAAM,KAAK,CAAC;IAChB,CAAC;CACJ;AAjCD,0DAiCC","sourcesContent":["import * as path from 'path';\nimport { RuleFailError, renderRuleFailForHuman } from '@webpieces/rules-config';\nimport type { RuntimeGraph } from './runtime-graph-model';\nimport { DEFAULT_RUNTIME_GOAL_PATH, RUNTIME_GOAL_INIT_COMMAND, RuntimeGoalFile } from './runtime-goal-file';\nimport { RuntimeComparisonView } from './graph-render-model';\nimport { RuntimeGoalComparison } from './runtime-goal-comparison';\nimport type { RuntimeVizOptions } from './runtime-viz-options';\nimport { toError } from '../toError';\n\n/**\n * Why Goal vs Current cannot be drawn (#1239, #1373): the full `reason` (console, tooltip, legend —\n * with its path and repair) and the short `menuNote` the Color-by menu shows under the dimmed item.\n */\nexport class RuntimeComparisonGap {\n constructor(\n public readonly reason: string,\n public readonly menuNote: string = reason,\n ) {}\n}\n\n/** The menu line under the dimmed Goal vs Current item when the goal file is absent (#1373). */\nexport const MISSING_GOAL_MENU_NOTE = `${path.posix.basename(DEFAULT_RUNTIME_GOAL_PATH)} does not exist`;\n\n/**\n * Loads the saved goal for the runtime page's comparison, turning a missing or unusable file into\n * the reason Goal vs Current is unavailable — never into \"no differences\".\n */\nexport class RuntimeComparisonInputs {\n constructor(private readonly goalFile: RuntimeGoalFile = new RuntimeGoalFile()) {}\n\n /** The saved goal, or why it cannot be compared (missing: how to start one). */\n goal(workspaceRoot: string): RuntimeGraph | RuntimeComparisonGap {\n if (!this.goalFile.exists(workspaceRoot))\n return new RuntimeComparisonGap(\n `No runtime goal at ${DEFAULT_RUNTIME_GOAL_PATH}. Start a proposal with: ${RUNTIME_GOAL_INIT_COMMAND}`,\n MISSING_GOAL_MENU_NOTE,\n );\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- an unusable comparison input becomes the mode's stated reason; the page itself still renders\n try {\n return this.goalFile.load(workspaceRoot);\n } catch (err: unknown) {\n const error = toError(err);\n return new RuntimeComparisonGap(this.text(error));\n }\n }\n\n view(current: RuntimeGraph, goal: RuntimeGraph | RuntimeComparisonGap, options: RuntimeVizOptions): RuntimeComparisonView {\n if (goal instanceof RuntimeComparisonGap) return this.unavailable(goal);\n return new RuntimeGoalComparison().build(current, goal, options);\n }\n\n /** Goal vs Current with no overlay: the mode is dimmed and says `gap` instead. */\n unavailable(gap: RuntimeComparisonGap): RuntimeComparisonView {\n return new RuntimeComparisonView(false, gap.reason, gap.menuNote, 'Current → Goal', '', null, {}, {}, []);\n }\n\n private text(error: Error): string {\n if (error instanceof RuleFailError) return renderRuleFailForHuman(error);\n throw error;\n }\n}\n"]}
@@ -11,6 +11,8 @@ export declare class RuntimeHtmlPage {
11
11
  private readonly navigation;
12
12
  constructor(clientJs?: () => string, filterJs?: () => string, sharedJs?: () => string);
13
13
  render(model: GraphRenderModel, title: string, graph: RuntimeGraph, options?: RuntimeVizOptions): string;
14
+ /** Goal vs Current is dimmed in the markup itself when its comparison is unavailable (#1373). */
15
+ private unavailableModes;
14
16
  private designLinks;
15
17
  private lockControl;
16
18
  private legend;
@@ -8,7 +8,6 @@ const graph_visualizer_1 = require("./graph-visualizer");
8
8
  const graph_render_model_1 = require("./graph-render-model");
9
9
  const runtime_details_1 = require("./runtime-details");
10
10
  const runtime_viz_options_1 = require("./runtime-viz-options");
11
- const runtime_goal_file_1 = require("./runtime-goal-file");
12
11
  const runtime_goal_details_1 = require("./runtime-goal-details");
13
12
  const runtime_goal_styles_1 = require("./runtime-goal-styles");
14
13
  const runtime_graph_diff_1 = require("./runtime-graph-diff");
@@ -45,7 +44,7 @@ class RuntimeHtmlPage {
45
44
  ...new Set(model.nodes.flatMap((node) => (node.tags === null ? [] : [node.tags.role]))),
46
45
  ].sort();
47
46
  const products = this.shownProducts(model);
48
- const parts = new graph_page_shell_1.ShellParts(title, this.lockControl(model), this.legend(model), this.edgeKey(), this.assets.html() + '<span id="wp-context-count"></span>', options.page === runtime_viz_options_1.RuntimePageKind.GOAL ? new runtime_goal_file_1.RuntimeGoalProvenance().html() : new saved_snapshot_1.SavedSnapshot().html(), this.responsibilities(graph, options), products, true, frameworks, roles, '<div class="wp-empty" id="wp-empty" hidden>No matching services. Open Filter and Clear all, or turn off Hide unconnected.</div>');
47
+ const parts = new graph_page_shell_1.ShellParts(title, this.lockControl(model), this.legend(model), this.edgeKey(), this.assets.html() + '<span id="wp-context-count"></span>', new saved_snapshot_1.SavedSnapshot().html(), this.responsibilities(graph, options), products, true, frameworks, roles, '<div class="wp-empty" id="wp-empty" hidden>No matching services. Open Filter and Clear all, or turn off Hide unconnected.</div>', this.unavailableModes(model));
49
48
  const shared = this.sharedJs()
50
49
  .split(graph_visualizer_2.CLIENT_MODEL_PLACEHOLDER)
51
50
  .join(this.assets.json(model))
@@ -67,6 +66,13 @@ class RuntimeHtmlPage {
67
66
  <script>${new runtime_goal_details_1.RuntimeGoalDetails(model.comparison).script()}</script>
68
67
  <script>${this.clientJs()}</script><script>${shared}</script></body></html>`;
69
68
  }
69
+ /** Goal vs Current is dimmed in the markup itself when its comparison is unavailable (#1373). */
70
+ unavailableModes(model) {
71
+ const comparison = model.comparison;
72
+ if (comparison === undefined || comparison === null || comparison.available)
73
+ return [];
74
+ return [new graph_color_modes_1.ModeUnavailable(graph_color_modes_1.GraphMode.GOAL, comparison.reason, comparison.menuNote)];
75
+ }
70
76
  designLinks(graph, options) {
71
77
  const links = [];
72
78
  for (const name of Object.keys(graph.services).sort()) {
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-html-page.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-html-page.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { DesignLink } from './graph-visualizer';\nimport { GraphRenderModel, GraphFilterAssets } from './graph-render-model';\nimport { RuntimeDetails } from './runtime-details';\nimport type { RuntimeGraph } from './runtime-graph-model';\nimport { RuntimeVizOptions, RuntimePageKind } from './runtime-viz-options';\nimport { RuntimeGoalProvenance } from './runtime-goal-file';\nimport { RuntimeGoalDetails } from './runtime-goal-details';\nimport { DIFF_STYLES } from './runtime-goal-styles';\nimport { RuntimeDiffStatus } from './runtime-graph-diff';\nimport { GraphNavigation, NavigationLayout } from './graph-navigation';\nimport { SavedSnapshot } from './saved-snapshot';\nimport {\n CLIENT_MODEL_PLACEHOLDER,\n CLIENT_DESIGN_LINKS_PLACEHOLDER,\n readCompiledClient,\n} from './graph-visualizer';\nimport { GraphNodeMenu } from './graph-node-menu';\nimport { GraphPageShell, ShellParts } from './graph-page-shell';\nimport { GraphPageStyles } from './graph-page-styles';\nimport { GraphLegend } from './graph-legend';\nimport { ProductColor } from './graph-products';\nimport { FRAMEWORK_STYLES, ROLE_STYLES } from './graph-color-modes';\nimport { IMPACT_SIDECAR_SRC } from './graph-impact';\nimport { ResponsibilitiesRenderer } from './graph-responsibilities';\nimport { htmlLabelText } from './dot-syntax';\nimport type { EnhancedGraph } from './graph-sorter';\n\n/** Runtime facts and shape adapter inside the same shell/controller as the project viewer. */\nexport class RuntimeHtmlPage {\n private readonly assets = new GraphFilterAssets();\n private readonly menu = new GraphNodeMenu();\n private readonly navigation = new GraphNavigation(NavigationLayout.FLOATING);\n\n constructor(\n private readonly clientJs: () => string = (): string =>\n readCompiledClient('runtime-visualizer.client.js'),\n private readonly filterJs: () => string = (): string =>\n readCompiledClient('graph-filter.client.js'),\n private readonly sharedJs: () => string = (): string =>\n readCompiledClient('graph-visualizer.client.js'),\n ) {}\n\n render(\n model: GraphRenderModel,\n title: string,\n graph: RuntimeGraph,\n options: RuntimeVizOptions = new RuntimeVizOptions(),\n ): string {\n model.viewer = 'runtime';\n const frameworks = [\n ...new Set(model.nodes.flatMap((node) => node.tags?.frameworks ?? [])),\n ].sort();\n const roles = [\n ...new Set(model.nodes.flatMap((node) => (node.tags === null ? [] : [node.tags.role]))),\n ].sort();\n const products = this.shownProducts(model);\n const parts = new ShellParts(\n title,\n this.lockControl(model),\n this.legend(model),\n this.edgeKey(),\n this.assets.html() + '<span id=\"wp-context-count\"></span>',\n options.page === RuntimePageKind.GOAL ? new RuntimeGoalProvenance().html() : new SavedSnapshot().html(),\n this.responsibilities(graph, options),\n products,\n true,\n frameworks,\n roles,\n '<div class=\"wp-empty\" id=\"wp-empty\" hidden>No matching services. Open Filter and Clear all, or turn off Hide unconnected.</div>',\n );\n const shared = this.sharedJs()\n .split(CLIENT_MODEL_PLACEHOLDER)\n .join(this.assets.json(model))\n .split(CLIENT_DESIGN_LINKS_PLACEHOLDER)\n .join(this.assets.json(this.designLinks(graph, options)));\n return `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${htmlLabelText(title)}</title>\n<script src=\"https://cdn.jsdelivr.net/npm/@viz-js/viz@3.28.0/dist/viz-global.js\"></script>\n<script src=\"${IMPACT_SIDECAR_SRC}\"></script>\n<style>${this.menu.styles()}${this.menu.dimStyles('#graph')}${this.assets.styles()}${this.navigation.styles()}${new GraphPageStyles().css()}\n.wp-graph-details, .wp-goal-details, .wp-impact-why { position:fixed; z-index:60; max-width:min(520px,calc(100vw - 24px)); max-height:70vh; overflow:auto; background:var(--wp-surface); border:1px solid var(--wp-line); border-radius:10px; padding:14px; box-shadow:0 8px 24px #0002; }\n.wp-graph-details button, .wp-goal-details button, .wp-impact-why button { cursor:pointer; } .wp-api-detail { cursor:pointer; text-decoration:underline; }\n.wp-api-detail:focus-visible { outline:3px solid var(--wp-accent); } #wp-context-count { font-size:11px; color:var(--wp-muted); }\n.wp-goal-edge { cursor:pointer; } .wp-goal-diff ul { margin:4px 0 0; padding-left:16px; max-height:220px; overflow:auto; }\n.wp-empty { position:absolute; top:70px; left:16px; padding:16px; background:var(--wp-surface); border-radius:10px; }\n</style></head><body data-wp-project-facts=\"${options.projects !== null}\">${new GraphPageShell().body(parts)}\n<script>${this.menu.script()}</script><script>${this.filterJs()}</script><script>${this.navigation.script()}</script>\n<script>${new RuntimeDetails(graph, options.showExternalNodes, model).script()}</script>\n<script>${new RuntimeGoalDetails(model.comparison).script()}</script>\n<script>${this.clientJs()}</script><script>${shared}</script></body></html>`;\n }\n\n private designLinks(graph: RuntimeGraph, options: RuntimeVizOptions): DesignLink[] {\n const links: DesignLink[] = [];\n for (const name of Object.keys(graph.services).sort()) {\n if (graph.services[name].drawOnGraph === false) continue;\n const design = options.projects?.[name]?.designFile?.replace(\n /design\\.json$/,\n 'design.html',\n );\n if (\n !design ||\n !options.workspaceRoot ||\n !fs.existsSync(path.join(options.workspaceRoot, design))\n )\n continue;\n links.push(new DesignLink(name, path.posix.relative('tmp/webpieces', design)));\n }\n return links;\n }\n\n private lockControl(model: GraphRenderModel): string {\n const entries = model.nodes\n .map(\n (node) =>\n `<option value=\"${htmlLabelText(node.id)}\">${htmlLabelText(node.id)} · ${node.tags?.role ?? 'context'}</option>`,\n )\n .join('');\n return `<input type=\"search\" id=\"wp-lock\" list=\"wp-lock-options\" placeholder=\"Lock a node… ( / )\" autocomplete=\"off\" spellcheck=\"false\"><datalist id=\"wp-lock-options\">${entries}</datalist>`;\n }\n\n private legend(model: GraphRenderModel): string {\n const tag = (group: string, value: string, color: string): string =>\n `<div class=\"wp-legend-row\" data-wp-legend-group=\"${group}\" data-wp-legend-value=\"${htmlLabelText(value)}\"><svg width=\"20\" height=\"14\"><rect width=\"20\" height=\"14\" rx=\"2\" fill=\"${color}\"/></svg>${htmlLabelText(value)}</div>`;\n const frameworks = Object.values(FRAMEWORK_STYLES)\n .filter((style) => model.legend.frameworks.includes(style.framework))\n .map((style) => tag('runtime', style.framework, style.color))\n .join('');\n const roles = [\n ...new Set(model.nodes.flatMap((node) => (node.tags === null ? [] : [node.tags.role]))),\n ]\n .map((role) =>\n tag(\n 'role',\n role,\n ROLE_STYLES.find((style) => style.role === role)?.fill ?? '#e4e6ec',\n ),\n )\n .join('');\n const legend = new GraphLegend();\n const products = this.shownProducts(model)\n .map((color) =>\n legend.productToggle(\n `data-wp-legend-group=\"product\" data-wp-legend-value=\"${htmlLabelText(color.product)}\"`,\n htmlLabelText(color.product),\n `<svg width=\"20\" height=\"14\"><rect width=\"20\" height=\"14\" rx=\"2\" fill=\"${color.color}\"/></svg>${htmlLabelText(color.product)}`,\n ),\n )\n .join('');\n return (\n `<section data-wp-legend-mode=\"runtime\" class=\"wp-legend-list\">${frameworks}${tag('runtime', 'unknown', '#e4e6ec')}<p>Neutral: unknown runtime. Nested: specialization; stripes: multiple runtimes. Context retains kind colors.</p></section>\n<section data-wp-legend-mode=\"architecture\" class=\"wp-legend-list\" hidden>${roles}<p>Auxiliary nodes use kind colors, never project roles.</p></section>` +\n `<section data-wp-legend-mode=\"impact\" class=\"wp-legend-list\" hidden>${legend.impactRows()}</section><section data-wp-legend-mode=\"product\" class=\"wp-legend-list\" hidden>${products}${legend.productNote()}<p>Queues, clocks, datastores and vendors inherit adjacent service memberships; several ON products are a gradient on them.</p></section>` +\n this.goalLegend(model) +\n '<p>Queues, clocks, datastores and vendors remain context in Impact; no source ownership or CI status is implied.</p>'\n );\n }\n\n /**\n * The products some drawn box carries, colored from the ONE palette the boxes were painted with —\n * built over every declared product (#1244), so a product keeps the dependency viewer's color even\n * though this page lists only the products it shows.\n */\n private shownProducts(model: GraphRenderModel): ProductColor[] {\n const shown = new Set(model.nodes.flatMap((node) => node.products));\n return model.legend.palette.colors.filter((color: ProductColor): boolean => shown.has(color.product));\n }\n\n /** Goal vs Current (#1239): the direction, the counts (or why it is unavailable), the key and every difference. */\n private goalLegend(model: GraphRenderModel): string {\n const comparison = model.comparison;\n if (comparison === null || !comparison.available)\n return `<section data-wp-legend-mode=\"goal\" class=\"wp-legend-list\" hidden><p>Goal vs Current unavailable: ${htmlLabelText(comparison?.reason ?? 'no comparison data on this page')}</p></section>`;\n const rows = [RuntimeDiffStatus.ADDED, RuntimeDiffStatus.REMOVED, RuntimeDiffStatus.CHANGED, RuntimeDiffStatus.UNCHANGED]\n .map((status: RuntimeDiffStatus) => {\n const style = DIFF_STYLES[status];\n const dash = status === RuntimeDiffStatus.REMOVED ? ' stroke-dasharray=\"3 2\"' : '';\n return `<div class=\"wp-legend-row wp-goal-row\"><svg width=\"20\" height=\"14\"><rect x=\"1\" y=\"1\" width=\"18\" height=\"12\" rx=\"2\" fill=\"${style.fill}\" stroke=\"${style.border}\" stroke-width=\"2\"${dash}/></svg>${style.word}</div>`;\n })\n .join('');\n const differences = comparison.differences.map((line: string) => `<li>${htmlLabelText(line)}</li>`).join('');\n return (\n `<section data-wp-legend-mode=\"goal\" class=\"wp-legend-list wp-goal-diff\" hidden><p><strong>${htmlLabelText(comparison.direction)}</strong> · ${htmlLabelText(comparison.summary)}</p>${rows}` +\n '<p>Arrows use the same colors; a removed arrow is dotted. A moved level reads L4 → L2. Click a box (Goal vs Current details) or an arrow for before/after.</p>' +\n (differences === '' ? '' : `<details><summary>Differences (${comparison.differences.length})</summary><ul>${differences}</ul></details>`) +\n '</section>'\n );\n }\n\n private edgeKey(): string {\n return '<div class=\"wp-legend-list\"><p>→ RPC · ⇢ queued/event · red: legacy cycle</p><p>Horizontal cylinder: queue<br>Upright cylinder: datastore<br>⏰: scheduler<br>Dashed box: external system</p></div>';\n }\n\n private responsibilities(graph: RuntimeGraph, options: RuntimeVizOptions): string {\n const projects: EnhancedGraph = {};\n for (const [name, service] of Object.entries(graph.services)) {\n projects[name] = {\n ...options.projects?.[name],\n level: service.level,\n dependsOn: service.dependsOn,\n drawOnGraph: service.drawOnGraph,\n };\n }\n return new ResponsibilitiesRenderer().generateSection(projects, options.workspaceRoot);\n }\n}\n"]}
1
+ {"version":3,"file":"runtime-html-page.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-html-page.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { DesignLink } from './graph-visualizer';\nimport { GraphRenderModel, GraphFilterAssets } from './graph-render-model';\nimport { RuntimeDetails } from './runtime-details';\nimport type { RuntimeGraph } from './runtime-graph-model';\nimport { RuntimeVizOptions } from './runtime-viz-options';\nimport { RuntimeGoalDetails } from './runtime-goal-details';\nimport { DIFF_STYLES } from './runtime-goal-styles';\nimport { RuntimeDiffStatus } from './runtime-graph-diff';\nimport { GraphNavigation, NavigationLayout } from './graph-navigation';\nimport { SavedSnapshot } from './saved-snapshot';\nimport {\n CLIENT_MODEL_PLACEHOLDER,\n CLIENT_DESIGN_LINKS_PLACEHOLDER,\n readCompiledClient,\n} from './graph-visualizer';\nimport { GraphNodeMenu } from './graph-node-menu';\nimport { GraphPageShell, ShellParts } from './graph-page-shell';\nimport { GraphPageStyles } from './graph-page-styles';\nimport { GraphLegend } from './graph-legend';\nimport { ProductColor } from './graph-products';\nimport { FRAMEWORK_STYLES, GraphMode, ModeUnavailable, ROLE_STYLES } from './graph-color-modes';\nimport { IMPACT_SIDECAR_SRC } from './graph-impact';\nimport { ResponsibilitiesRenderer } from './graph-responsibilities';\nimport { htmlLabelText } from './dot-syntax';\nimport type { EnhancedGraph } from './graph-sorter';\n\n/** Runtime facts and shape adapter inside the same shell/controller as the project viewer. */\nexport class RuntimeHtmlPage {\n private readonly assets = new GraphFilterAssets();\n private readonly menu = new GraphNodeMenu();\n private readonly navigation = new GraphNavigation(NavigationLayout.FLOATING);\n\n constructor(\n private readonly clientJs: () => string = (): string =>\n readCompiledClient('runtime-visualizer.client.js'),\n private readonly filterJs: () => string = (): string =>\n readCompiledClient('graph-filter.client.js'),\n private readonly sharedJs: () => string = (): string =>\n readCompiledClient('graph-visualizer.client.js'),\n ) {}\n\n render(\n model: GraphRenderModel,\n title: string,\n graph: RuntimeGraph,\n options: RuntimeVizOptions = new RuntimeVizOptions(),\n ): string {\n model.viewer = 'runtime';\n const frameworks = [\n ...new Set(model.nodes.flatMap((node) => node.tags?.frameworks ?? [])),\n ].sort();\n const roles = [\n ...new Set(model.nodes.flatMap((node) => (node.tags === null ? [] : [node.tags.role]))),\n ].sort();\n const products = this.shownProducts(model);\n const parts = new ShellParts(\n title,\n this.lockControl(model),\n this.legend(model),\n this.edgeKey(),\n this.assets.html() + '<span id=\"wp-context-count\"></span>',\n new SavedSnapshot().html(),\n this.responsibilities(graph, options),\n products,\n true,\n frameworks,\n roles,\n '<div class=\"wp-empty\" id=\"wp-empty\" hidden>No matching services. Open Filter and Clear all, or turn off Hide unconnected.</div>',\n this.unavailableModes(model),\n );\n const shared = this.sharedJs()\n .split(CLIENT_MODEL_PLACEHOLDER)\n .join(this.assets.json(model))\n .split(CLIENT_DESIGN_LINKS_PLACEHOLDER)\n .join(this.assets.json(this.designLinks(graph, options)));\n return `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${htmlLabelText(title)}</title>\n<script src=\"https://cdn.jsdelivr.net/npm/@viz-js/viz@3.28.0/dist/viz-global.js\"></script>\n<script src=\"${IMPACT_SIDECAR_SRC}\"></script>\n<style>${this.menu.styles()}${this.menu.dimStyles('#graph')}${this.assets.styles()}${this.navigation.styles()}${new GraphPageStyles().css()}\n.wp-graph-details, .wp-goal-details, .wp-impact-why { position:fixed; z-index:60; max-width:min(520px,calc(100vw - 24px)); max-height:70vh; overflow:auto; background:var(--wp-surface); border:1px solid var(--wp-line); border-radius:10px; padding:14px; box-shadow:0 8px 24px #0002; }\n.wp-graph-details button, .wp-goal-details button, .wp-impact-why button { cursor:pointer; } .wp-api-detail { cursor:pointer; text-decoration:underline; }\n.wp-api-detail:focus-visible { outline:3px solid var(--wp-accent); } #wp-context-count { font-size:11px; color:var(--wp-muted); }\n.wp-goal-edge { cursor:pointer; } .wp-goal-diff ul { margin:4px 0 0; padding-left:16px; max-height:220px; overflow:auto; }\n.wp-empty { position:absolute; top:70px; left:16px; padding:16px; background:var(--wp-surface); border-radius:10px; }\n</style></head><body data-wp-project-facts=\"${options.projects !== null}\">${new GraphPageShell().body(parts)}\n<script>${this.menu.script()}</script><script>${this.filterJs()}</script><script>${this.navigation.script()}</script>\n<script>${new RuntimeDetails(graph, options.showExternalNodes, model).script()}</script>\n<script>${new RuntimeGoalDetails(model.comparison).script()}</script>\n<script>${this.clientJs()}</script><script>${shared}</script></body></html>`;\n }\n\n /** Goal vs Current is dimmed in the markup itself when its comparison is unavailable (#1373). */\n private unavailableModes(model: GraphRenderModel): ModeUnavailable[] {\n const comparison = model.comparison;\n if (comparison === undefined || comparison === null || comparison.available) return [];\n return [new ModeUnavailable(GraphMode.GOAL, comparison.reason, comparison.menuNote)];\n }\n\n private designLinks(graph: RuntimeGraph, options: RuntimeVizOptions): DesignLink[] {\n const links: DesignLink[] = [];\n for (const name of Object.keys(graph.services).sort()) {\n if (graph.services[name].drawOnGraph === false) continue;\n const design = options.projects?.[name]?.designFile?.replace(\n /design\\.json$/,\n 'design.html',\n );\n if (\n !design ||\n !options.workspaceRoot ||\n !fs.existsSync(path.join(options.workspaceRoot, design))\n )\n continue;\n links.push(new DesignLink(name, path.posix.relative('tmp/webpieces', design)));\n }\n return links;\n }\n\n private lockControl(model: GraphRenderModel): string {\n const entries = model.nodes\n .map(\n (node) =>\n `<option value=\"${htmlLabelText(node.id)}\">${htmlLabelText(node.id)} · ${node.tags?.role ?? 'context'}</option>`,\n )\n .join('');\n return `<input type=\"search\" id=\"wp-lock\" list=\"wp-lock-options\" placeholder=\"Lock a node… ( / )\" autocomplete=\"off\" spellcheck=\"false\"><datalist id=\"wp-lock-options\">${entries}</datalist>`;\n }\n\n private legend(model: GraphRenderModel): string {\n const tag = (group: string, value: string, color: string): string =>\n `<div class=\"wp-legend-row\" data-wp-legend-group=\"${group}\" data-wp-legend-value=\"${htmlLabelText(value)}\"><svg width=\"20\" height=\"14\"><rect width=\"20\" height=\"14\" rx=\"2\" fill=\"${color}\"/></svg>${htmlLabelText(value)}</div>`;\n const frameworks = Object.values(FRAMEWORK_STYLES)\n .filter((style) => model.legend.frameworks.includes(style.framework))\n .map((style) => tag('runtime', style.framework, style.color))\n .join('');\n const roles = [\n ...new Set(model.nodes.flatMap((node) => (node.tags === null ? [] : [node.tags.role]))),\n ]\n .map((role) =>\n tag(\n 'role',\n role,\n ROLE_STYLES.find((style) => style.role === role)?.fill ?? '#e4e6ec',\n ),\n )\n .join('');\n const legend = new GraphLegend();\n const products = this.shownProducts(model)\n .map((color) =>\n legend.productToggle(\n `data-wp-legend-group=\"product\" data-wp-legend-value=\"${htmlLabelText(color.product)}\"`,\n htmlLabelText(color.product),\n `<svg width=\"20\" height=\"14\"><rect width=\"20\" height=\"14\" rx=\"2\" fill=\"${color.color}\"/></svg>${htmlLabelText(color.product)}`,\n ),\n )\n .join('');\n return (\n `<section data-wp-legend-mode=\"runtime\" class=\"wp-legend-list\">${frameworks}${tag('runtime', 'unknown', '#e4e6ec')}<p>Neutral: unknown runtime. Nested: specialization; stripes: multiple runtimes. Context retains kind colors.</p></section>\n<section data-wp-legend-mode=\"architecture\" class=\"wp-legend-list\" hidden>${roles}<p>Auxiliary nodes use kind colors, never project roles.</p></section>` +\n `<section data-wp-legend-mode=\"impact\" class=\"wp-legend-list\" hidden>${legend.impactRows()}</section><section data-wp-legend-mode=\"product\" class=\"wp-legend-list\" hidden>${products}${legend.productNote()}<p>Queues, clocks, datastores and vendors inherit adjacent service memberships; several ON products are a gradient on them.</p></section>` +\n this.goalLegend(model) +\n '<p>Queues, clocks, datastores and vendors remain context in Impact; no source ownership or CI status is implied.</p>'\n );\n }\n\n /**\n * The products some drawn box carries, colored from the ONE palette the boxes were painted with —\n * built over every declared product (#1244), so a product keeps the dependency viewer's color even\n * though this page lists only the products it shows.\n */\n private shownProducts(model: GraphRenderModel): ProductColor[] {\n const shown = new Set(model.nodes.flatMap((node) => node.products));\n return model.legend.palette.colors.filter((color: ProductColor): boolean => shown.has(color.product));\n }\n\n /** Goal vs Current (#1239): the direction, the counts (or why it is unavailable), the key and every difference. */\n private goalLegend(model: GraphRenderModel): string {\n const comparison = model.comparison;\n if (comparison === null || !comparison.available)\n return `<section data-wp-legend-mode=\"goal\" class=\"wp-legend-list\" hidden><p>Goal vs Current unavailable: ${htmlLabelText(comparison?.reason ?? 'no comparison data on this page')}</p></section>`;\n const rows = [RuntimeDiffStatus.ADDED, RuntimeDiffStatus.REMOVED, RuntimeDiffStatus.CHANGED, RuntimeDiffStatus.UNCHANGED]\n .map((status: RuntimeDiffStatus) => {\n const style = DIFF_STYLES[status];\n const dash = status === RuntimeDiffStatus.REMOVED ? ' stroke-dasharray=\"3 2\"' : '';\n return `<div class=\"wp-legend-row wp-goal-row\"><svg width=\"20\" height=\"14\"><rect x=\"1\" y=\"1\" width=\"18\" height=\"12\" rx=\"2\" fill=\"${style.fill}\" stroke=\"${style.border}\" stroke-width=\"2\"${dash}/></svg>${style.word}</div>`;\n })\n .join('');\n const differences = comparison.differences.map((line: string) => `<li>${htmlLabelText(line)}</li>`).join('');\n return (\n `<section data-wp-legend-mode=\"goal\" class=\"wp-legend-list wp-goal-diff\" hidden><p><strong>${htmlLabelText(comparison.direction)}</strong> · ${htmlLabelText(comparison.summary)}</p>${rows}` +\n '<p>Arrows use the same colors; a removed arrow is dotted. A moved level reads L4 → L2. Click a box (Goal vs Current details) or an arrow for before/after.</p>' +\n (differences === '' ? '' : `<details><summary>Differences (${comparison.differences.length})</summary><ul>${differences}</ul></details>`) +\n '</section>'\n );\n }\n\n private edgeKey(): string {\n return '<div class=\"wp-legend-list\"><p>→ RPC · ⇢ queued/event · red: legacy cycle</p><p>Horizontal cylinder: queue<br>Upright cylinder: datastore<br>⏰: scheduler<br>Dashed box: external system</p></div>';\n }\n\n private responsibilities(graph: RuntimeGraph, options: RuntimeVizOptions): string {\n const projects: EnhancedGraph = {};\n for (const [name, service] of Object.entries(graph.services)) {\n projects[name] = {\n ...options.projects?.[name],\n level: service.level,\n dependsOn: service.dependsOn,\n drawOnGraph: service.drawOnGraph,\n };\n }\n return new ResponsibilitiesRenderer().generateSection(projects, options.workspaceRoot);\n }\n}\n"]}
@@ -11,8 +11,6 @@ import type { RuntimeGraph } from './runtime-graph-model';
11
11
  */
12
12
  export declare class RuntimeUnionGraph {
13
13
  build(current: RuntimeGraph, goal: RuntimeGraph): RuntimeGraph;
14
- /** Edges matched on (transport, from, to, queue); a matched edge carries both graphs' contracts. */
15
- private edges;
16
14
  private missing;
17
15
  private externals;
18
16
  private remotes;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RuntimeUnionGraph = void 0;
4
+ const runtime_edge_merge_1 = require("./runtime-edge-merge");
4
5
  /**
5
6
  * The OVERLAY drawn by Goal vs Current (#1239): every goal element, plus every element only the
6
7
  * current graph has, so removals stay visible instead of silently disappearing.
@@ -23,7 +24,8 @@ class RuntimeUnionGraph {
23
24
  union.queues[key] ??= queue;
24
25
  for (const [api, queue] of Object.entries(old.contractQueues))
25
26
  union.contractQueues[api] ??= queue;
26
- union.runtimeEdges = this.edges(union.runtimeEdges, old.runtimeEdges);
27
+ // Matched on (transport, from, to, queue); a matched edge carries both graphs' contracts.
28
+ union.runtimeEdges = new runtime_edge_merge_1.RuntimeEdgeMerge().merge([...union.runtimeEdges, ...old.runtimeEdges]);
27
29
  union.triggers = this.missing(union.triggers, old.triggers, (trigger) => `${trigger.kind} ${trigger.api}.${trigger.method} ${trigger.service}`);
28
30
  union.unresolvedUses = this.missing(union.unresolvedUses, old.unresolvedUses, (use) => `${use.service} ${use.api}`);
29
31
  this.externals(union, old);
@@ -31,24 +33,6 @@ class RuntimeUnionGraph {
31
33
  union.maxLevel = Math.max(union.maxLevel, old.maxLevel);
32
34
  return union;
33
35
  }
34
- /** Edges matched on (transport, from, to, queue); a matched edge carries both graphs' contracts. */
35
- edges(goal, current) {
36
- const key = (edge) => `${edge.type ?? 'rpc'} ${edge.from} ${edge.to} ${edge.queue ?? ''}`;
37
- const byKey = new Map();
38
- for (const edge of goal)
39
- byKey.set(key(edge), edge);
40
- const out = [...goal];
41
- for (const edge of current) {
42
- const match = byKey.get(key(edge));
43
- if (match === undefined) {
44
- byKey.set(key(edge), edge);
45
- out.push(edge);
46
- }
47
- else
48
- match.via = [...new Set([...match.via, ...edge.via])].sort();
49
- }
50
- return out;
51
- }
52
36
  missing(goal, current, key) {
53
37
  const seen = new Set(goal.map(key));
54
38
  return [...goal, ...current.filter((value) => !seen.has(key(value)))];
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-union-graph.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-union-graph.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n RuntimeEdge,\n RuntimeGraph,\n RuntimeRemoteCall,\n RuntimeTrigger,\n RuntimeUnresolved,\n} from './runtime-graph-model';\n\n/**\n * The OVERLAY drawn by Goal vs Current (#1239): every goal element, plus every element only the\n * current graph has, so removals stay visible instead of silently disappearing.\n *\n * Presentation only. It is never levelled, never cycle-checked and never saved: an overlay of two\n * architectures can contain a cycle neither input has, and that is not an architectural finding.\n * Every element keeps the level its OWN graph saved — retained and added elements sit at their goal\n * level, removed ones at their current level — so the shared renderer's strict level bands still\n * order the rows and only real upward edges become non-constraining feedback edges.\n */\nexport class RuntimeUnionGraph {\n build(current: RuntimeGraph, goal: RuntimeGraph): RuntimeGraph {\n const union = this.copy(goal);\n const old = this.copy(current);\n for (const [name, service] of Object.entries(old.services)) union.services[name] ??= service;\n for (const [name, api] of Object.entries(old.apis)) union.apis[name] ??= api;\n for (const [key, queue] of Object.entries(old.queues)) union.queues[key] ??= queue;\n for (const [api, queue] of Object.entries(old.contractQueues)) union.contractQueues[api] ??= queue;\n union.runtimeEdges = this.edges(union.runtimeEdges, old.runtimeEdges);\n union.triggers = this.missing(union.triggers, old.triggers, (trigger: RuntimeTrigger) => `${trigger.kind} ${trigger.api}.${trigger.method} ${trigger.service}`);\n union.unresolvedUses = this.missing(union.unresolvedUses, old.unresolvedUses, (use: RuntimeUnresolved) => `${use.service} ${use.api}`);\n this.externals(union, old);\n this.remotes(union, old);\n union.maxLevel = Math.max(union.maxLevel, old.maxLevel);\n return union;\n }\n\n /** Edges matched on (transport, from, to, queue); a matched edge carries both graphs' contracts. */\n private edges(goal: RuntimeEdge[], current: RuntimeEdge[]): RuntimeEdge[] {\n const key = (edge: RuntimeEdge): string => `${edge.type ?? 'rpc'} ${edge.from} ${edge.to} ${edge.queue ?? ''}`;\n const byKey = new Map<string, RuntimeEdge>();\n for (const edge of goal) byKey.set(key(edge), edge);\n const out = [...goal];\n for (const edge of current) {\n const match = byKey.get(key(edge));\n if (match === undefined) {\n byKey.set(key(edge), edge);\n out.push(edge);\n } else match.via = [...new Set([...match.via, ...edge.via])].sort();\n }\n return out;\n }\n\n private missing<T>(goal: T[], current: T[], key: (value: T) => string): T[] {\n const seen = new Set(goal.map(key));\n return [...goal, ...current.filter((value: T) => !seen.has(key(value)))];\n }\n\n private externals(union: RuntimeGraph, old: RuntimeGraph): void {\n if (union.externalSystems === undefined && old.externalSystems === undefined) return;\n const systems = (union.externalSystems ??= {});\n for (const [id, system] of Object.entries(old.externalSystems ?? {})) {\n const kept = systems[id];\n if (kept === undefined) {\n systems[id] = system;\n continue;\n }\n kept.usedBy = [...new Set([...kept.usedBy, ...system.usedBy])].sort();\n kept.apis = [...new Set([...kept.apis, ...system.apis])].sort();\n }\n }\n\n private remotes(union: RuntimeGraph, old: RuntimeGraph): void {\n if (union.remoteServices === undefined && old.remoteServices === undefined) return;\n const remotes = (union.remoteServices ??= {});\n for (const [name, remote] of Object.entries(old.remoteServices ?? {})) {\n const kept = remotes[name];\n if (kept === undefined) {\n remotes[name] = remote;\n continue;\n }\n for (const call of remote.calledBy) {\n const match = kept.calledBy.find((entry: RuntimeRemoteCall) => entry.from === call.from && entry.type === call.type);\n if (match === undefined) kept.calledBy.push(call);\n else match.via = [...new Set([...match.via, ...call.via])].sort();\n }\n }\n }\n\n private copy(graph: RuntimeGraph): RuntimeGraph {\n return JSON.parse(JSON.stringify(graph)) as RuntimeGraph;\n }\n}\n"]}
1
+ {"version":3,"file":"runtime-union-graph.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-union-graph.ts"],"names":[],"mappings":"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","sourcesContent":["import { RuntimeEdgeMerge } from './runtime-edge-merge';\nimport type {\n RuntimeGraph,\n RuntimeRemoteCall,\n RuntimeTrigger,\n RuntimeUnresolved,\n} from './runtime-graph-model';\n\n/**\n * The OVERLAY drawn by Goal vs Current (#1239): every goal element, plus every element only the\n * current graph has, so removals stay visible instead of silently disappearing.\n *\n * Presentation only. It is never levelled, never cycle-checked and never saved: an overlay of two\n * architectures can contain a cycle neither input has, and that is not an architectural finding.\n * Every element keeps the level its OWN graph saved — retained and added elements sit at their goal\n * level, removed ones at their current level — so the shared renderer's strict level bands still\n * order the rows and only real upward edges become non-constraining feedback edges.\n */\nexport class RuntimeUnionGraph {\n build(current: RuntimeGraph, goal: RuntimeGraph): RuntimeGraph {\n const union = this.copy(goal);\n const old = this.copy(current);\n for (const [name, service] of Object.entries(old.services)) union.services[name] ??= service;\n for (const [name, api] of Object.entries(old.apis)) union.apis[name] ??= api;\n for (const [key, queue] of Object.entries(old.queues)) union.queues[key] ??= queue;\n for (const [api, queue] of Object.entries(old.contractQueues)) union.contractQueues[api] ??= queue;\n // Matched on (transport, from, to, queue); a matched edge carries both graphs' contracts.\n union.runtimeEdges = new RuntimeEdgeMerge().merge([...union.runtimeEdges, ...old.runtimeEdges]);\n union.triggers = this.missing(union.triggers, old.triggers, (trigger: RuntimeTrigger) => `${trigger.kind} ${trigger.api}.${trigger.method} ${trigger.service}`);\n union.unresolvedUses = this.missing(union.unresolvedUses, old.unresolvedUses, (use: RuntimeUnresolved) => `${use.service} ${use.api}`);\n this.externals(union, old);\n this.remotes(union, old);\n union.maxLevel = Math.max(union.maxLevel, old.maxLevel);\n return union;\n }\n\n private missing<T>(goal: T[], current: T[], key: (value: T) => string): T[] {\n const seen = new Set(goal.map(key));\n return [...goal, ...current.filter((value: T) => !seen.has(key(value)))];\n }\n\n private externals(union: RuntimeGraph, old: RuntimeGraph): void {\n if (union.externalSystems === undefined && old.externalSystems === undefined) return;\n const systems = (union.externalSystems ??= {});\n for (const [id, system] of Object.entries(old.externalSystems ?? {})) {\n const kept = systems[id];\n if (kept === undefined) {\n systems[id] = system;\n continue;\n }\n kept.usedBy = [...new Set([...kept.usedBy, ...system.usedBy])].sort();\n kept.apis = [...new Set([...kept.apis, ...system.apis])].sort();\n }\n }\n\n private remotes(union: RuntimeGraph, old: RuntimeGraph): void {\n if (union.remoteServices === undefined && old.remoteServices === undefined) return;\n const remotes = (union.remoteServices ??= {});\n for (const [name, remote] of Object.entries(old.remoteServices ?? {})) {\n const kept = remotes[name];\n if (kept === undefined) {\n remotes[name] = remote;\n continue;\n }\n for (const call of remote.calledBy) {\n const match = kept.calledBy.find((entry: RuntimeRemoteCall) => entry.from === call.from && entry.type === call.type);\n if (match === undefined) kept.calledBy.push(call);\n else match.via = [...new Set([...match.via, ...call.via])].sort();\n }\n }\n }\n\n private copy(graph: RuntimeGraph): RuntimeGraph {\n return JSON.parse(JSON.stringify(graph)) as RuntimeGraph;\n }\n}\n"]}
@@ -12,8 +12,7 @@ export interface RuntimeVisualizationPaths {
12
12
  htmlPath: string;
13
13
  }
14
14
  /**
15
- * Write the DOT + HTML renderings to tmp/webpieces/: `runtime-architecture.*` for the current graph,
16
- * `runtime-architecture-goal.*` for the goal (#1239), so neither page ever overwrites the other.
17
- * `comparison` is the Goal vs Current overlay the page carries (or why it is unavailable).
15
+ * Write the DOT + HTML renderings to tmp/webpieces/runtime-architecture.*. `comparison` is the
16
+ * Goal vs Current overlay the page carries (or why it is unavailable, #1239).
18
17
  */
19
18
  export declare function writeRuntimeVisualization(graph: RuntimeGraph, workspaceRoot: string, title: string, options: RuntimeVizOptions, comparison: RuntimeComparisonView): RuntimeVisualizationPaths;
@@ -512,9 +512,8 @@ function generateRuntimeRenderModel(graph, title = 'WebPieces Runtime Architectu
512
512
  return model;
513
513
  }
514
514
  /**
515
- * Write the DOT + HTML renderings to tmp/webpieces/: `runtime-architecture.*` for the current graph,
516
- * `runtime-architecture-goal.*` for the goal (#1239), so neither page ever overwrites the other.
517
- * `comparison` is the Goal vs Current overlay the page carries (or why it is unavailable).
515
+ * Write the DOT + HTML renderings to tmp/webpieces/runtime-architecture.*. `comparison` is the
516
+ * Goal vs Current overlay the page carries (or why it is unavailable, #1239).
518
517
  */
519
518
  function writeRuntimeVisualization(graph, workspaceRoot, title, options, comparison) {
520
519
  const outputDir = path.join(workspaceRoot, 'tmp', 'webpieces');
@@ -523,7 +522,7 @@ function writeRuntimeVisualization(graph, workspaceRoot, title, options, compari
523
522
  const model = generateRuntimeRenderModel(graph, title, options);
524
523
  model.comparison = comparison;
525
524
  const dot = model.fullDot;
526
- const base = options.page === runtime_viz_options_1.RuntimePageKind.GOAL ? 'runtime-architecture-goal' : 'runtime-architecture';
525
+ const base = 'runtime-architecture';
527
526
  const dotPath = path.join(outputDir, `${base}.dot`);
528
527
  fs.writeFileSync(dotPath, dot, 'utf-8');
529
528
  const htmlPath = path.join(outputDir, `${base}.html`);