@webpieces/nx-webpieces-rules 0.4.971 → 0.4.973

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 (52) 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-goal-comparison.js +1 -1
  25. package/src/lib/runtime-goal-comparison.js.map +1 -1
  26. package/src/lib/runtime-goal-file.d.ts +6 -11
  27. package/src/lib/runtime-goal-file.js +7 -23
  28. package/src/lib/runtime-goal-file.js.map +1 -1
  29. package/src/lib/runtime-goal-validator.d.ts +4 -4
  30. package/src/lib/runtime-goal-validator.js +4 -4
  31. package/src/lib/runtime-goal-validator.js.map +1 -1
  32. package/src/lib/runtime-goal-view.d.ts +18 -11
  33. package/src/lib/runtime-goal-view.js +27 -27
  34. package/src/lib/runtime-goal-view.js.map +1 -1
  35. package/src/lib/runtime-html-page.d.ts +2 -0
  36. package/src/lib/runtime-html-page.js +8 -2
  37. package/src/lib/runtime-html-page.js.map +1 -1
  38. package/src/lib/runtime-visualizer.d.ts +2 -3
  39. package/src/lib/runtime-visualizer.js +3 -4
  40. package/src/lib/runtime-visualizer.js.map +1 -1
  41. package/src/lib/runtime-viz-options.d.ts +3 -5
  42. package/src/lib/runtime-viz-options.js +3 -7
  43. package/src/lib/runtime-viz-options.js.map +1 -1
  44. package/src/plugin.js +0 -1
  45. package/src/plugin.js.map +1 -1
  46. package/src/runtime-targets.d.ts +0 -6
  47. package/src/runtime-targets.js +1 -18
  48. package/src/runtime-targets.js.map +1 -1
  49. package/src/executors/visualize-runtime-goal/executor.d.ts +0 -27
  50. package/src/executors/visualize-runtime-goal/executor.js +0 -99
  51. package/src/executors/visualize-runtime-goal/executor.js.map +0 -1
  52. 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"]}
@@ -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`);
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-visualizer.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-visualizer.ts"],"names":[],"mappings":";;;AAqlBA,gDAMC;AAID,gEA+DC;AAYD,8DAqBC;;AA/rBD,qDAAiD;AACjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,+CAAyB;AACzB,mDAA6B;AAQ7B,6CAAqE;AACrE,6DAAwD;AACxD,qEAAgE;AAChE,2DAAsD;AACtD,2EAAsE;AACtE,qEAA+D;AAC/D,2DAc6B;AAE7B,+FAA+F;AAC/F,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAE9B,iGAAiG;AACjG,MAAM,QAAQ,GAAG,GAAG,CAAC;AAErB,+DAA2E;AAE3E,6DAA2E;AAAlE,wHAAA,iBAAiB,OAAA;AAAE,sHAAA,eAAe,OAAA;AAE3C;;;;;;GAMG;AACH,SAAS,YAAY,CAAC,IAAY;IAC9B,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9D,CAAC;AAED,gGAAgG;AAChG,oGAAoG;AACpG,SAAS,aAAa,CAAC,IAAY;IAC/B,OAAO,IAAA,qBAAQ,EAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,2FAA2F;AAC3F,uGAAuG;AACvG,SAAS,SAAS,CAAC,OAAiB;IAChC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,KAAa,EAAE,EAAE,CAAC,IAAA,qBAAQ,EAAC,KAAK,CAAC,CAAC,CAAC;IAC7D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,mBAAmB,EAAE,CAAC;QACxD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,mBAAmB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;;;;;GASG;AACH,wGAAwG;AACxG,SAAS,OAAO,CAAC,IAAiB;IAC9B,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,EAAE,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClC,MAAM,MAAM,GACR,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC;QACf,CAAC,CAAC,SAAS,IAAI,CAAC,GAAG,CAAC,MAAM,KAAK;QAC/B,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClE,MAAM,GAAG,GAAG,IAAI,CAAC,cAAc,KAAK,IAAI,CAAC;IACzC,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,IAAA,qBAAQ,EAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAA,qBAAQ,EAAC,MAAM,CAAC,CAAC;IAC5E,OAAO,MAAM,IAAI,SAAS,EAAE,aAAa,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,wCAAoB,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;AAC5F,CAAC;AAED,sGAAsG;AACtG,MAAM,cAAc;IACP,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;CAC1C;AAED;;;;GAIG;AACH,MAAM,UAAU;IAEQ;IACA;IACA;IAHpB,YACoB,OAAiB,EACjB,SAAmB,EACnB,SAAmB;QAFnB,YAAO,GAAP,OAAO,CAAU;QACjB,cAAS,GAAT,SAAS,CAAU;QACnB,cAAS,GAAT,SAAS,CAAU;IACpC,CAAC;CACP;AAED;;;;;;;;GAQG;AACH,uGAAuG;AACvG,SAAS,SAAS,CAAC,GAAW,EAAE,KAA+B;IAC3D,MAAM,IAAI,GAAG,IAAA,wBAAW,EAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,KAAK,CAAC,SAAS,KAAK,GAAG,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,MAAM,EAAE;QAAE,OAAO,IAAI,CAAC;IACpE,OAAO,GAAG,IAAI,aAAa,IAAA,wBAAW,EAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;AAC9D,CAAC;AAED,iIAAiI;AACjI,wGAAwG;AACxG,SAAS,WAAW,CAChB,EAAU,EACV,IAAY,EACZ,SAAmB,EACnB,SAAmB,EACnB,KAAuB,EACvB,SAAmB,EACnB,KAAa;IAEb,4FAA4F;IAC5F,uFAAuF;IACvF,2BAA2B;IAC3B,IAAI,GAAG,GAAG,KAAK,CAAC,IAAI,CAChB,EAAE,EACF,MAAM,EAAE,YAAY,+BAAW,gCAAgC,8BAAU,KAAK;QAC1E,UAAU,+BAAW,aAAa,sCAAkB,IAAI,KAAK,KAAK,IAAI,OAAO,EACjF,EAAE,EACF,SAAS,CACZ,CAAC;IACF,KAAK,MAAM,QAAQ,IAAI,SAAS;QAC5B,GAAG,IAAI,KAAK,CAAC,IAAI,CACb,QAAQ,EACR,EAAE,EACF,MAAM,aAAa,CAAC,QAAQ,CAAC,SAAS,EAAE,sCAAsC,CACjF,CAAC;IACN,KAAK,MAAM,QAAQ,IAAI,SAAS;QAC5B,GAAG,IAAI,KAAK,CAAC,IAAI,CACb,EAAE,EACF,QAAQ,EACR,MAAM,EAAE,SAAS,aAAa,CAAC,QAAQ,CAAC,qBAAqB,CAChE,CAAC;IACN,OAAO,GAAG,CAAC;AACf,CAAC;AAED;;;;;;;;;GASG;AACH,wGAAwG;AACxG,SAAS,WAAW,CAChB,OAAoC,EACpC,KAAmB,EACnB,IAA4B;IAE5B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC7C,8FAA8F;IAC9F,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAC3C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC;QAC/B,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7C,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACxD,MAAM,QAAQ,GAAG;YACb,GAAG;YACH,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK;YACvB,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;YACnB,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;YACnB,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;SAClB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACjB,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACtC,IAAI,QAAQ,KAAK,SAAS;YACtB,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,UAAU,CAAC,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;;YACjE,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;AAChC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,wGAAwG;AACxG,SAAS,SAAS,CACd,KAAmB,EACnB,MAAmB,EACnB,KAAuB,EACvB,OAA0B;IAE1B,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,CACpC,CAAC,CAAc,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CACtF,CAAC;IACF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEnC,IAAI,GAAG,GACH,2FAA2F;QAC3F,4EAA4E,CAAC;IAEjF,2FAA2F;IAC3F,MAAM,UAAU,GAAG,IAAI,GAAG,EAA0B,CAAC;IACrD,MAAM,OAAO,GAAG,IAAI,GAAG,EAA0B,CAAC;IAClD,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC;QAClE,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACrB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,cAAc,EAAE,CAAC,CAAC;YAClE,SAAS,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7C,SAAS,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC/C,CAAC;IACL,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAC9C,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC;QAClC,GAAG,IAAI,WAAW,CACd,UAAU,KAAK,CAAC,GAAG,CAAC,EAAE,EACtB,GAAG,IAAA,wBAAW,EAAC,YAAY,CAAC,GAAG,CAAC,CAAC,QAAQ,EACzC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAC1B,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAC1B,KAAK,EACL,CAAC,SAAS,GAAG,EAAE,CAAC,EAChB,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,KAAK,CAClC,CAAC;IACN,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;QACtE,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO;aACrB,GAAG,CAAC,CAAC,GAAW,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;aACvD,IAAI,CAAC,KAAK,CAAC,CAAC;QACjB,GAAG,IAAI,WAAW,CACd,UAAU,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,EACnC,IAAI,EACJ,KAAK,CAAC,SAAS,EACf,KAAK,CAAC,SAAS,EACf,KAAK,EACL,KAAK,CAAC,OAAO,EACb,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CACvC,CAAC;IACN,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,qFAAqF;AACrF,oGAAoG;AACpG,SAAS,KAAK,CAAC,GAAW;IACtB,OAAO,GAAG,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;;GAOG;AACH,wGAAwG;AACxG,SAAS,qBAAqB,CAC1B,QAAgB,EAChB,IAAY,EACZ,KAAa,EACb,QAAgB,EAChB,KAAuB;IAEvB,MAAM,KAAK,GAAG,mCAAe,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC;IAC7C,MAAM,IAAI,GAAG,kCAAc,CAAC,IAAI,CAAC,IAAI,iCAAa,CAAC;IACnD,gGAAgG;IAChG,6DAA6D;IAC7D,MAAM,OAAO,GAAG,KAAK,KAAK,+BAAW,CAAC;IACtC,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,sCAAkB,CAAC,CAAC,CAAC,EAAE,CAAC;IACjD,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,IAAA,wBAAW,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAA,qBAAQ,EAAC,KAAK,CAAC,CAAC;IAC5D,6FAA6F;IAC7F,uEAAuE;IACvE,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,UAAU,+BAAW,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IACzD,OAAO,KAAK,CAAC,IAAI,CACb,WAAW,KAAK,CAAC,QAAQ,CAAC,EAAE,EAC5B,cAAc,KAAK,CAAC,QAAQ,CAAC,YAAY,KAAK,gCAAgC,IAAI,KAAK;QACnF,GAAG,MAAM,UAAU,MAAM,GAAG,IAAI,OAAO,QAAQ,QAAQ,CAC9D,CAAC;AACN,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wGAAwG;AACxG,SAAS,UAAU,CAAC,KAAmB,EAAE,MAAmB,EAAE,KAAuB;IACjF,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAiB,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACtF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAErC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,IAAI,GAAG,GACH,0FAA0F,CAAC;IAC/F,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAuB,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,EAAE,CAAC;QAC1F,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC/C,MAAM,KAAK,GAAG,IAAA,qBAAQ,EAAC,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAC3D,MAAM,EAAE,GAAG,SAAS,KAAK,CAAC,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;QAChE,MAAM,QAAQ,GAAG,IAAA,qBAAQ,EAAC,OAAO,CAAC,SAAS,IAAI,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACnF,GAAG;YACC,KAAK,CAAC,IAAI,CACN,EAAE,EACF,MAAM,EAAE,+CAA+C,6BAAS,KAAK;gBACjE,UAAU,+BAAW,yBAAyB,CACrD;gBACD,KAAK,CAAC,IAAI,CACN,EAAE,EACF,OAAO,CAAC,OAAO,EACf,MAAM,EAAE,SAAS,OAAO,aAAa,KAAK,MAAM,QAAQ,aAAa,+BAAW,OAAO,CAC1F,CAAC;IACV,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,IAAI,mDAAuB,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC;QAC/D,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACxC,MAAM,KAAK,GAAG,IAAA,qBAAQ,EAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC9B,6FAA6F;QAC7F,6FAA6F;QAC7F,kCAAkC;QAClC,MAAM,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YACnB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChB,GAAG;gBACC,MAAM,KAAK,SAAS;oBAChB,CAAC,CAAC,KAAK,CAAC,IAAI,CACN,EAAE,EACF,MAAM,EAAE,mDAAmD,iCAAa,KAAK;wBACzE,UAAU,mCAAe,aAAa,IAAA,qBAAQ,EAAC,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAC5F;oBACH,CAAC,CAAC,qBAAqB,CACjB,WAAW,MAAM,CAAC,KAAK,EAAE,EACzB,MAAM,CAAC,IAAI,EACX,MAAM,CAAC,KAAK,EACZ,iBAAiB,EACjB,KAAK,CACR,CAAC;QAChB,CAAC;QACD,GAAG,IAAI,KAAK,CAAC,IAAI,CACb,EAAE,EACF,OAAO,CAAC,OAAO,EACf,MAAM,EAAE,SAAS,OAAO,aAAa,KAAK,2BAA2B,mCAAe,OAAO,CAC9F,CAAC;IACN,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED;;;;;;;;;GASG;AACH,wGAAwG;AACxG,SAAS,kBAAkB,CACvB,KAAmB,EACnB,MAAmB,EACnB,KAAuB;IAEvB,MAAM,OAAO,GAAG,KAAK,CAAC,eAAe,IAAI,EAAE,CAAC;IAC5C,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IACxC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEhC,IAAI,GAAG,GACH,sFAAsF,CAAC;IAC3F,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACnB,MAAM,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;QAC3B,GAAG,IAAI,qBAAqB,CACxB,EAAE,EACF,MAAM,CAAC,IAAI,EACX,MAAM,CAAC,KAAK,EACZ,YAAY,IAAA,qBAAQ,EAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EACnC,KAAK,CACR,CAAC;IACN,CAAC;IACD,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACnB,MAAM,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;QAC3B,MAAM,GAAG,GACL,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;YACpB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,YAAY,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC;QACzH,KAAK,MAAM,OAAO,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YAC9C,IAAI,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC;gBAAE,SAAS;YAClC,GAAG,IAAI,KAAK,CAAC,IAAI,CACb,OAAO,EACP,WAAW,KAAK,CAAC,EAAE,CAAC,EAAE,EACtB,MAAM,aAAa,CAAC,OAAO,CAAC,iBAAiB,KAAK,CAAC,EAAE,CAAC,IAAI,GAAG,KAAK,CACrE,CAAC;QACN,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED;;;;;;;;;;;GAWG;AACH,wGAAwG;AACxG,SAAS,WAAW,CAAC,KAAmB,EAAE,MAAmB,EAAE,KAAuB;IAClF,sDAAsD;IACtD,MAAM,UAAU,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC/C,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC;QACrC,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,SAAS;QACtC,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,cAAc,KAAK,SAAS;YAAE,SAAS;QAChE,wFAAwF;QACxF,gFAAgF;QAChF,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC,GAAG,CAAC;QACvD,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,OAAO,GAAG,QAAQ,GAAG,QAAQ,EAAE,CAAC;QACnD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAClD,UAAU,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACvC,CAAC;IACD,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAErC,IAAI,GAAG,GAAG,qFAAqF,CAAC;IAChG,6FAA6F;IAC7F,2FAA2F;IAC3F,yBAAyB;IACzB,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/F,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAC3C,GAAG,IAAI,KAAK,CAAC,IAAI,CACb,aAAa,QAAQ,EAAE,EACvB,gBAAgB,IAAA,qBAAQ,EAAC,QAAQ,CAAC,mDAAmD,iCAAa,KAAK;YACnG,UAAU,mCAAe,aAAa,IAAA,qBAAQ,EAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CACjG,CAAC;IACN,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAC9C,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAClC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACzB,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAC1B,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,IAAI,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC1E,6FAA6F;QAC7F,kEAAkE;QAClE,GAAG,IAAI,KAAK,CAAC,IAAI,CACb,OAAO,EACP,aAAa,QAAQ,EAAE,EACvB,MAAM,aAAa,CAAC,OAAO,CAAC,mBAAmB,IAAA,qBAAQ,EAAC,QAAQ,CAAC,IAAI;YACjE,WAAW,GAAG,aAAa,mCAAe,OAAO,CACxD,CAAC;IACN,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,wGAAwG;AACxG,SAAS,iBAAiB,CACtB,KAAmB,EACnB,MAAmB,EACnB,KAAuB;IAEvB,MAAM,OAAO,GAAG,KAAK,CAAC,cAAc,IAAI,EAAE,CAAC;IAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAElC,IAAI,GAAG,GACH,wFAAwF,CAAC;IAC7F,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,EAAE,GAAG,IAAI,4CAAmB,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QAC9C,8FAA8F;QAC9F,MAAM,KAAK,GACP,MAAM,CAAC,KAAK,KAAK,SAAS;YACtB,CAAC,CAAC,IAAA,qBAAQ,EAAC,IAAI,CAAC;YAChB,CAAC,CAAC,GAAG,IAAA,qBAAQ,EAAC,MAAM,CAAC,KAAK,CAAC,MAAM,IAAA,qBAAQ,EAAC,IAAI,CAAC,EAAE,CAAC;QAC1D,GAAG,IAAI,KAAK,CAAC,IAAI,CACb,EAAE,EACF,MAAM,EAAE,yDAAyD,uCAAmB,KAAK;YACrF,UAAU,yCAAqB,aAAa,KAAK,wBAAwB;YACzE,eAAe,MAAM,CAAC,UAAU,CAAC,MAAM,QAAQ,CACtD,CAAC;IACN,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,IAAI,4CAAmB,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QAC9C,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;YACxC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,SAAS;YACpC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtF,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,GAAG,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC;YAChE,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7D,GAAG,IAAI,KAAK,CAAC,IAAI,CACb,IAAI,CAAC,IAAI,EACT,EAAE,EACF,MAAM,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,KAAK,aAAa,yCAAqB,IAAI,KAAK,MAAM,CAC/G,CAAC;QACN,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,4DAA4D;AAC5D,wGAAwG;AACxG,SAAgB,kBAAkB,CAC9B,KAAmB,EACnB,QAAgB,gCAAgC,EAChD,UAA6B,IAAI,uCAAiB,EAAE;IAEpD,OAAO,0BAA0B,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC;AACrE,CAAC;AAED,mFAAmF;AACnF,kGAAkG;AAClG,SAAgB,0BAA0B,CACtC,KAAmB,EACnB,QAAgB,gCAAgC,EAChD,UAA6B,IAAI,uCAAiB,EAAE;IAEpD,MAAM,KAAK,GAAG,IAAI,qCAAgB,EAAE,CAAC;IACrC,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC;IACzB,MAAM,OAAO,GAAG,IAAI,6CAAoB,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClE,IAAI,GAAG,GAAG,iCAAiC,CAAC;IAC5C,GAAG,IAAI,iBAAiB,CAAC;IACzB,GAAG,IAAI,iEAAiE,CAAC;IACzE,GAAG,IAAI,6CAA6C,CAAC;IAErD,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC;IAEnB,4EAA4E;IAC5E,0EAA0E;IAC1E,MAAM,MAAM,GAAG,IAAI,GAAG,CAClB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,CAC9B,CAAC,IAAY,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,KAAK,KAAK,CAC/D,CACJ,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7C,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QAC/B,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACjC,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;IAC7C,CAAC;IAED,GAAG,IAAI,IAAI,CAAC;IAEZ,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QACpC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAAE,SAAS;QAC3D,uFAAuF;QACvF,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;YAAE,SAAS;QACrC,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACzD,CAAC;IAED,GAAG,IAAI,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IAChD,GAAG,IAAI,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAC/C,GAAG,IAAI,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAExC,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;QAC5B,GAAG,IAAI,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;QAChD,GAAG,IAAI,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC;IAC/B,GAAG,IAAI,qBAAqB,CAAC;IAC7B,GAAG,IAAI,YAAY,IAAA,qBAAQ,EAAC,KAAK,CAAC,YAAY,IAAA,qBAAQ,EAAC,OAAO,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC;IAChF,GAAG,IAAI,kBAAkB,CAAC;IAC1B,GAAG,IAAI,KAAK,CAAC;IACb,6FAA6F;IAC7F,iGAAiG;IACjG,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACtC,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC1B,iGAAiG;IACjG,KAAK,CAAC,cAAc,CAAC,CAAC,EAAU,EAAW,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,SAAS,CAAC,CAAC;IAChF,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAC5B,KAAK,CAAC,OAAO,GAAG,IAAI,8BAAa,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClD,IAAA,2BAAc,EAAC,KAAK,CAAC,OAAO,EAAE,0BAA0B,CAAC,CAAC;IAC1D,OAAO,KAAK,CAAC;AACjB,CAAC;AAOD;;;;GAIG;AACH,SAAgB,yBAAyB,CACrC,KAAmB,EACnB,aAAqB,EACrB,KAAa,EACb,OAA0B,EAC1B,UAAiC;IAEjC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;IAC/D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5E,MAAM,KAAK,GAAG,0BAA0B,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IAChE,KAAK,CAAC,UAAU,GAAG,UAAU,CAAC;IAC9B,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC;IAC1B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,KAAK,qCAAe,CAAC,IAAI,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,sBAAsB,CAAC;IAC1G,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,MAAM,CAAC,CAAC;IACpD,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAExC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,OAAO,CAAC,CAAC;IACtD,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,mCAAe,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;IAEhG,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AACjC,CAAC","sourcesContent":["import { RuntimeLayout } from './runtime-layout';\n/**\n * Runtime Visualizer\n *\n * Renders the runtime microservice graph (services + inferred Z -> X edges,\n * each labeled with the api(s) they flow over) to DOT + interactive HTML in\n * tmp/webpieces/runtime-architecture.{dot,html}.\n *\n * Each service node names the contracts it IMPLEMENTS. That list used to be\n * collapsed into a server/client boolean and thrown away — leaving an api that a\n * server serves but nothing in-repo calls completely invisible, and making a\n * correct api design look like a detection failure. What a service USES is NOT\n * listed: every use already draws an outgoing arrow labeled with the same\n * contract, so repeating it in the box only made every box wider.\n *\n * Shape says what a node IS and line style says what a call IS. Solid = rpc: the\n * request follows the arrow and the response flows back. Dashed = event: it flows\n * in the arrow's direction and returns once it is queued. A queue is a sideways\n * cylinder, a datastore an upright one. A queue box lists ONE LINE PER QUEUE: the\n * unit underneath is still the METHOD (what Terraform creates, and what\n * runtime-dependencies.json records), but queues of one contract sharing the same\n * producers and consumers are drawn together instead of as adjacent near-identical\n * boxes — every queue is still named, only the node count drops.\n *\n * Calls that leave the repo (a contract NOTHING in-repo implements — firestore,\n * gmail, ...) are drawn as terminal nodes, so the vendor systems that actually\n * page you at 3am stop being missing from the picture. One that DECLARES what it\n * is, via an `@externalSystem` JSDoc tag or an `external:` nx tag, gets the shape\n * of the thing it is; the rest stay generic dashed boxes. They are RENDER-ONLY:\n * derivation, levels and cycle detection never see them.\n *\n * The same is true in the other direction for endpoints nothing in-repo CALLS: a\n * `cron` method hangs off a clock and an `external` method off a box naming the\n * CALLER that posts to it (`twilio`), separately from an outbound sink for the same\n * vendor, so callers can sit on the top row and sinks on L0. Those are the\n * entry points that wake a service up at 3am, and a graph built only from in-repo\n * callers cannot show them at all.\n *\n * EVERY node on the rendered page is clickable and opens the SHARED floating menu\n * (graph-node-menu.ts — one implementation, also used by architecture/dependencies.html\n * and every project's design.html). Lock/Unlock pins separate complete incoming/outgoing chains, with hover adding a temporary chain. There is no \"View Design\"\n * item here — a node is a running service, queue, datastore or third-party system, not\n * an nx project, so no design.html exists to point at and the item is absent rather than\n * dead. Filter Unconnected follows separate incoming/outgoing chains and compacts the rendered\n * topology; Turn off Filter restores the full picture. Lock remains independent of Filter.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport type {\n RuntimeGraph,\n RuntimeEdge,\n RuntimeQueue,\n RuntimeService,\n RuntimeTrigger,\n} from './runtime-graph';\nimport { dotValue, recordValue, assertValidDot } from './dot-syntax';\nimport { GraphRenderModel } from './graph-render-model';\nimport { RuntimeViewerAdapter } from './runtime-viewer-adapter';\nimport { RuntimeHtmlPage } from './runtime-html-page';\nimport { RuntimeExternalTriggers } from './runtime-external-triggers';\nimport { RemoteServiceNodeId } from './remote-service-node-id';\nimport {\n QUEUE_FILL,\n QUEUE_SHAPE,\n QUEUE_LABEL_PREFIX,\n QUEUE_CLASS,\n EXTERNAL_SHAPES,\n EXTERNAL_FILLS,\n EXTERNAL_FILL,\n EXTERNAL_BORDER,\n CRON_FILL,\n CRON_BORDER,\n CUT_CYCLE_EDGE_ATTRS,\n REMOTE_SERVICE_FILL,\n REMOTE_SERVICE_BORDER,\n} from './runtime-viz-theme';\n\n/** Apis per line inside a node label — beyond this the box grows wider than it is readable. */\nconst APIS_PER_LABEL_LINE = 3;\n\n/** Separator for the (service, external-library) grouping key; illegal in both project names. */\nconst PAIR_SEP = '|';\n\nimport { RuntimeVizOptions, RuntimePageKind } from './runtime-viz-options';\nimport type { RuntimeComparisonView } from './graph-render-model';\nexport { RuntimeVizOptions, RuntimePageKind } from './runtime-viz-options';\n\n/**\n * The DISPLAY name of a project/service: scope stripped. LABELS ONLY.\n *\n * Never an identity. `@scope/public-api` and `public-api` are two distinct projects that strip to\n * the same string, and keying nodes or edges on that fuses them into one box (and draws the edge\n * between them as a self-loop). See {@link serviceNodeId} and lib/graph-names.ts.\n */\nfunction getShortName(name: string): string {\n return name.includes('/') ? name.split('/').pop()! : name;\n}\n\n/** The DOT identity of a runtime service: its project key, escaped — never the display name. */\n// webpieces-disable no-function-outside-class -- DOT id builder, matching getShortName in this file\nfunction serviceNodeId(name: string): string {\n return dotValue(name);\n}\n\n/** Chunk a list into `\\n`-separated label lines of at most APIS_PER_LABEL_LINE entries. */\n// webpieces-disable no-function-outside-class -- DOT label builder, matching getShortName in this file\nfunction labelList(entries: string[]): string {\n const lines: string[] = [];\n const safe = entries.map((entry: string) => dotValue(entry));\n for (let i = 0; i < safe.length; i += APIS_PER_LABEL_LINE) {\n lines.push(safe.slice(i, i + APIS_PER_LABEL_LINE).join(', '));\n }\n return lines.join('\\\\n');\n}\n\n/**\n * DOT for ONE NON-QUEUED runtime edge: a direct labeled SOLID arrow (producer calls consumer, the\n * response comes back). Queued (pubsub) hops are drawn by {@link queuesDot} instead, because they\n * are merged across methods and therefore cannot be emitted one edge at a time.\n *\n * Dashed is the graph's one line-level distinction, and it now says one of two things: an event that\n * returns as soon as it is queued, or (in red, labelled \"legacy cycle\") an edge a `cutLegacyCycle:`\n * tag holds out of leveling. A cut edge is still DRAWN — erasing the arrow would erase exactly the\n * debt the tag records — it is only drawn as the debt it is. See lib/cut-legacy-cycle-resolver.ts.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction edgeDot(edge: RuntimeEdge): string {\n const from = serviceNodeId(edge.from);\n const to = serviceNodeId(edge.to);\n const viaRaw =\n edge.via.length > 1\n ? `Uses (${edge.via.length}) ▾`\n : edge.via.map((v: string) => getShortName(v)).join(', ');\n const cut = edge.cutLegacyCycle === true;\n const label = cut ? `${dotValue(viaRaw)}\\\\nlegacy cycle` : dotValue(viaRaw);\n return ` \"${from}\" -> \"${to}\" [label=\"${label}\"${cut ? CUT_CYCLE_EDGE_ATTRS : ''}];\\n`;\n}\n\n/** The VISIBLE producers and consumers of one queue, gathered from the edges that survived hiding. */\nclass QueueEndpoints {\n readonly producers = new Set<string>();\n readonly consumers = new Set<string>();\n}\n\n/**\n * One drawn queue BOX: the queue keys (`Api.method`) it shows, and the endpoints they all share.\n * Several keys land in one box only when they agree on contract, producers, consumers and\n * their persisted level — see {@link queuesDot}.\n */\nclass QueueGroup {\n constructor(\n public readonly members: string[],\n public readonly producers: string[],\n public readonly consumers: string[],\n ) {}\n}\n\n/**\n * The label line for ONE queue inside a box: `Api.method`, and the Terraform queue name ONLY when it\n * is not the derived `${Api}-${method}`.\n *\n * Printing a derived name was pure restatement — the line above it already said `Api.method`, so\n * every queue box carried a second line that added nothing and doubled its height. A `@Queue(...)`\n * OVERRIDE is the opposite: that string appears nowhere else on the graph, and it is the one string\n * Terraform must match, so it stays.\n */\n// webpieces-disable no-function-outside-class -- DOT label builder, matching getShortName in this file\nfunction queueLine(key: string, queue: RuntimeQueue | undefined): string {\n const line = recordValue(key);\n if (queue === undefined) return line;\n if (queue.queueName === `${queue.api}-${queue.method}`) return line;\n return `${line}\\\\nqueue: ${recordValue(queue.queueName)}`;\n}\n\n/** The node statement + enqueue/deliver arrows for one queue box. Service identities remain raw; only emitted DOT is escaped. */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction queueBoxDot(\n id: string,\n body: string,\n producers: string[],\n consumers: string[],\n model: GraphRenderModel,\n queueKeys: string[],\n level: number,\n): string {\n // Record-mode label: the text must clear recordValue(), and QUEUE_LABEL_PREFIX supplies the\n // empty leading field that draws the cylinder's end cap. Drop it and the node silently\n // degrades to a plain box.\n let dot = model.node(\n id,\n ` \"${id}\" [shape=${QUEUE_SHAPE}, style=\"filled\", fillcolor=\"${QUEUE_FILL}\", ` +\n `class=\"${QUEUE_CLASS}\", label=\"${QUEUE_LABEL_PREFIX}L${level} ${body}\"];\\n`,\n [],\n queueKeys,\n );\n for (const producer of producers)\n dot += model.edge(\n producer,\n id,\n ` \"${serviceNodeId(producer)}\" -> \"${id}\" [label=\"enqueue\", style=dashed];\\n`,\n );\n for (const consumer of consumers)\n dot += model.edge(\n id,\n consumer,\n ` \"${id}\" -> \"${serviceNodeId(consumer)}\" [style=dashed];\\n`,\n );\n return dot;\n}\n\n/**\n * Group the queued hops into boxes. The key is (contract, producer SET, consumer SET) — NOT the\n * (from,to) pair of some edge. Persisted levels must also match: hiding a foreign producer\n * can make a foreign queue look self-only without changing its generated level.\n *\n * A queue node converges EVERY producer and consumer of its method onto one box, so keying by a\n * service PAIR would re-split a two-producer queue into two boxes and invent topology nobody wrote.\n * Only queues of the same contract whose producer and consumer sets are IDENTICAL may share a box;\n * anything else stays separate, because merging it would assert a routing that does not exist.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction groupQueues(\n byQueue: Map<string, QueueEndpoints>,\n graph: RuntimeGraph,\n tags: Record<string, string>,\n): QueueGroup[] {\n const groups = new Map<string, QueueGroup>();\n // Sorted keys → the first member of every group, and therefore its node id, is deterministic.\n for (const key of [...byQueue.keys()].sort()) {\n const ends = byQueue.get(key)!;\n const producers = [...ends.producers].sort();\n const consumers = [...ends.consumers].sort();\n const api = graph.queues[key]?.api ?? key.split('.')[0];\n const mergeKey = [\n api,\n graph.queues[key].level,\n producers.join(','),\n consumers.join(','),\n tags[key] ?? '',\n ].join(PAIR_SEP);\n const existing = groups.get(mergeKey);\n if (existing === undefined)\n groups.set(mergeKey, new QueueGroup([key], producers, consumers));\n else existing.members.push(key);\n }\n return [...groups.values()];\n}\n\n/**\n * The queued half of the graph: producer → QUEUE → consumer, drawn with a sideways-cylinder node and\n * DASHED arrows (an event returns as soon as it is queued).\n *\n * Contract-only edges converge on one box per API across all producers and consumers.\n * For exact method declarations, one BOX may list several queues, one per line. The unit Cloud Tasks and Terraform create is still\n * the METHOD — that is what runtime-dependencies.json records and this render never changes it — but\n * two methods of one contract flowing between exactly the same producers and consumers drew two\n * adjacent boxes whose only difference was the method name, and each carried a `queue:` line that\n * merely restated it. Listing them inside ONE box keeps every queue named (you can still see which\n * one is stuck) at a fraction of the node count.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction queuesDot(\n graph: RuntimeGraph,\n hidden: Set<string>,\n model: GraphRenderModel,\n options: RuntimeVizOptions,\n): string {\n const queued = graph.runtimeEdges.filter(\n (e: RuntimeEdge) => e.type === 'pubsub' && !hidden.has(e.from) && !hidden.has(e.to),\n );\n if (queued.length === 0) return '';\n\n let dot =\n '\\n // Queued hops. Each LINE in a box is one Cloud Tasks queue; queues of one contract\\n' +\n ' // sharing the same producers AND consumers are drawn in a single box.\\n';\n\n // Contract-only declarations converge by API, independently of the enqueuer/consumer pair.\n const byContract = new Map<string, QueueEndpoints>();\n const byQueue = new Map<string, QueueEndpoints>();\n for (const edge of queued) {\n const keys = edge.queue === undefined ? edge.via : [edge.queue];\n const endpoints = edge.queue === undefined ? byContract : byQueue;\n for (const key of keys) {\n if (!endpoints.has(key)) endpoints.set(key, new QueueEndpoints());\n endpoints.get(key)!.producers.add(edge.from);\n endpoints.get(key)!.consumers.add(edge.to);\n }\n }\n for (const api of [...byContract.keys()].sort()) {\n const ends = byContract.get(api)!;\n dot += queueBoxDot(\n `queue__${dotId(api)}`,\n `${recordValue(getShortName(api))} queue`,\n [...ends.producers].sort(),\n [...ends.consumers].sort(),\n model,\n [`queue:${api}`],\n graph.contractQueues[api].level,\n );\n }\n\n for (const group of groupQueues(byQueue, graph, options.queueGroupTags)) {\n const body = group.members\n .map((key: string) => queueLine(key, graph.queues[key]))\n .join('\\\\n');\n dot += queueBoxDot(\n `queue__${dotId(group.members[0])}`,\n body,\n group.producers,\n group.consumers,\n model,\n group.members,\n graph.queues[group.members[0]].level,\n );\n }\n return dot;\n}\n\n/** A DOT-safe node-id fragment: anything but letters, digits and `_` becomes `_`. */\n// webpieces-disable no-function-outside-class -- DOT id builder, matching getShortName in this file\nfunction dotId(raw: string): string {\n return raw.replace(/[^A-Za-z0-9_]/g, '_');\n}\n\n/**\n * ONE external-system node statement: the shape/fill of what the system IS, its label, and a\n * parenthesised subtitle saying which way it faces.\n *\n * Shared by the OUTBOUND systems ({@link externalSystemsDot}) and the INBOUND callers\n * ({@link triggerDot}) on purpose. Callers use a separate identity from outbound sinks\n * because their generated levels differ, while sharing the same shape and color palette.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction externalSystemNodeDot(\n identity: string,\n kind: string,\n label: string,\n subtitle: string,\n model: GraphRenderModel,\n): string {\n const shape = EXTERNAL_SHAPES[kind] ?? 'box';\n const fill = EXTERNAL_FILLS[kind] ?? EXTERNAL_FILL;\n // An Mrecord-shaped system needs the same empty leading field as a queue node, or it renders as\n // a plain box and silently loses the sideways-cylinder read.\n const isQueue = shape === QUEUE_SHAPE;\n const prefix = isQueue ? QUEUE_LABEL_PREFIX : '';\n const text = isQueue ? recordValue(label) : dotValue(label);\n // Only a queue-kind system is marked: a database here is an UPRIGHT cylinder and must not be\n // caught by the browser-side reshaping that lays queues on their side.\n const marker = isQueue ? `class=\"${QUEUE_CLASS}\", ` : '';\n return model.node(\n `system__${dotId(identity)}`,\n ` \"system__${dotId(identity)}\" [shape=${shape}, style=\"filled\", fillcolor=\"${fill}\", ` +\n `${marker}label=\"${prefix}${text}\\\\n(${subtitle})\"];\\n`,\n );\n}\n\n/**\n * The clock and outside-system nodes for endpoints NOTHING in-repo calls.\n *\n * A cron sweep and a GCP push subscription are real runtime entry points with real Terraform behind\n * them, but they produce no runtime EDGE (there is no in-repo caller), so until now they were simply\n * absent — a server's most operationally interesting endpoint could be invisible on its own graph.\n * Both are drawn pointing INTO the service that serves them, the opposite direction from\n * {@link externalDot}'s outbound vendor calls.\n *\n * The inbound box names the CALLER (`twilio`), never the contract. Naming the contract was the whole\n * bug: it restated what the service box directly below already says, while the one fact the box\n * exists to convey — which vendor is posting to us — appeared nowhere. The contract keeps its place\n * on the EDGE label, where `WhatsAppApi.inbound` reads as \"…posts to this method\".\n *\n * Inbound identities are separate from outbound sinks so each has its own generated level.\n * Several methods called by the same vendor still share one caller box.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction triggerDot(graph: RuntimeGraph, hidden: Set<string>, model: GraphRenderModel): string {\n const triggers = graph.triggers.filter((t: RuntimeTrigger) => !hidden.has(t.service));\n if (triggers.length === 0) return '';\n\n const emitted = new Set<string>();\n let dot =\n '\\n // Entry points nothing in this repo calls: a clock, or a system outside the repo.\\n';\n for (const trigger of triggers.filter((trigger: RuntimeTrigger) => trigger.kind === 'cron')) {\n const service = serviceNodeId(trigger.service);\n const label = dotValue(`${trigger.api}.${trigger.method}`);\n const id = `cron__${dotId(`${trigger.api}_${trigger.method}`)}`;\n const schedule = dotValue(trigger.queueName ?? `${trigger.api}-${trigger.method}`);\n dot +=\n model.node(\n id,\n ` \"${id}\" [shape=circle, style=\"filled\", fillcolor=\"${CRON_FILL}\", ` +\n `color=\"${CRON_BORDER}\", label=\"⏰\\\\ncron\"];\\n`,\n ) +\n model.edge(\n id,\n trigger.service,\n ` \"${id}\" -> \"${service}\" [label=\"${label}\\\\n${schedule}\", color=\"${CRON_BORDER}\"];\\n`,\n );\n }\n for (const group of new RuntimeExternalTriggers(triggers).groups) {\n const trigger = group.triggers[0];\n const service = serviceNodeId(group.to);\n const label = dotValue(group.label());\n const caller = trigger.caller;\n // Undeclared: only possible for a graph generated BEFORE the caller was required (generation\n // now fails instead). DOTTED and question-marked, so \"we were never told who this is\" cannot\n // be mistaken for a named vendor.\n const id = group.from;\n if (!emitted.has(id)) {\n emitted.add(id);\n dot +=\n caller === undefined\n ? model.node(\n id,\n ` \"${id}\" [shape=box, style=\"dotted,filled\", fillcolor=\"${EXTERNAL_FILL}\", ` +\n `color=\"${EXTERNAL_BORDER}\", label=\"${dotValue(trigger.api)}\\\\n? unknown caller\"];\\n`,\n )\n : externalSystemNodeDot(\n `caller__${caller.label}`,\n caller.kind,\n caller.label,\n 'external caller',\n model,\n );\n }\n dot += model.edge(\n id,\n trigger.service,\n ` \"${id}\" -> \"${service}\" [label=\"${label}\", style=dashed, color=\"${EXTERNAL_BORDER}\"];\\n`,\n );\n }\n return dot;\n}\n\n/**\n * The DECLARED external systems — the ones that said what they are, so they get a shape that says\n * it: a cylinder for a database, a folder for a bucket. Everything undeclared falls through to\n * {@link externalDot}'s generic grey box, which is why adding this broke nothing existing.\n *\n * The arrows are SOLID. A call to firestore or postgres is synchronous — it returns a value — and\n * the graph's rule is that solid means \"response comes back\". Being outside the repo is carried by\n * the node's shape, never by the line style; conflating the two is what made a blocking database\n * read look like an event.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction externalSystemsDot(\n graph: RuntimeGraph,\n hidden: Set<string>,\n model: GraphRenderModel,\n): string {\n const systems = graph.externalSystems ?? {};\n const ids = Object.keys(systems).sort();\n if (ids.length === 0) return '';\n\n let dot =\n '\\n // Declared external systems — drawn with the shape of what they actually are.\\n';\n for (const id of ids) {\n const system = systems[id];\n dot += externalSystemNodeDot(\n id,\n system.kind,\n system.label,\n `external ${dotValue(system.kind)}`,\n model,\n );\n }\n for (const id of ids) {\n const system = systems[id];\n const via =\n system.apis.length === 0\n ? ''\n : ` [label=\"${system.apis.length > 1 ? `Uses (${system.apis.length}) ▾` : labelList([...system.apis].sort())}\"]`;\n for (const service of [...system.usedBy].sort()) {\n if (hidden.has(service)) continue;\n dot += model.edge(\n service,\n `system__${dotId(id)}`,\n ` \"${serviceNodeId(service)}\" -> \"system__${dotId(id)}\"${via};\\n`,\n );\n }\n }\n return dot;\n}\n\n/**\n * The dashed terminal nodes + edges for calls that LEAVE the repo. Built from `unresolvedUses` —\n * a contract used by a node and implemented by nobody in-repo — which the derivation already\n * computes and which was, until now, only ever printed as a warning.\n *\n * Grouped by the api-lib that owns the contracts, so a service reaching three firestore contracts\n * draws ONE `lib-firestore (external)` box rather than three. The unresolved uses carry their\n * terminal level in the generated graph; grouping changes presentation only.\n *\n * A contract carrying an `@externalSystem` declaration is skipped here — {@link externalSystemsDot}\n * has already drawn it with a real shape, and rendering it in both places would double the node.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction externalDot(graph: RuntimeGraph, hidden: Set<string>, model: GraphRenderModel): string {\n // \"service|externalName\" -> the apis flowing over it.\n const apisByPair = new Map<string, string[]>();\n for (const use of graph.unresolvedUses) {\n if (hidden.has(use.service)) continue;\n if (graph.apis[use.api]?.externalSystem !== undefined) continue;\n // Keyed on the OWNER's full name, never its short name: two scoped api-libs whose short\n // names match are two external systems, and fusing them draws one box for both.\n const external = graph.apis[use.api]?.owner ?? use.api;\n const key = `${use.service}${PAIR_SEP}${external}`;\n if (!apisByPair.has(key)) apisByPair.set(key, []);\n apisByPair.get(key)!.push(use.api);\n }\n if (apisByPair.size === 0) return '';\n\n let dot = '\\n // Systems outside this repo — no in-repo service implements these contracts.\\n';\n // The node ID is prefixed so an external library can never collide with a service, and built\n // from the owner's FULL name so two same-short-named owners stay two boxes; only the label\n // carries the bare name.\n const externals = new Set([...apisByPair.keys()].map((key: string) => key.split(PAIR_SEP)[1]));\n for (const external of [...externals].sort()) {\n dot += model.node(\n `external__${external}`,\n ` \"external__${dotValue(external)}\" [shape=box, style=\"dashed,filled\", fillcolor=\"${EXTERNAL_FILL}\", ` +\n `color=\"${EXTERNAL_BORDER}\", label=\"${dotValue(getShortName(external))}\\\\n(external)\"];\\n`,\n );\n }\n for (const key of [...apisByPair.keys()].sort()) {\n const parts = key.split(PAIR_SEP);\n const service = parts[0];\n const external = parts[1];\n const apis = apisByPair.get(key)!.sort();\n const via = apis.length > 1 ? `Uses (${apis.length}) ▾` : labelList(apis);\n // SOLID: this is a synchronous call that returns a value. Dashed is reserved for events, and\n // \"outside the repo\" is already said by the node's dashed border.\n dot += model.edge(\n service,\n `external__${external}`,\n ` \"${serviceNodeId(service)}\" -> \"external__${dotValue(external)}\" ` +\n `[label=\"${via}\", color=\"${EXTERNAL_BORDER}\"];\\n`,\n );\n }\n return dot;\n}\n\n/**\n * The reviewed out-of-workspace services (#1203): service-shaped nodes (rounded, teal, bold outline,\n * \"(remote service)\"), with an arrow from every workspace service that targets one. An rpc call is a\n * solid arrow and a queued call a dashed \"enqueue\" arrow, exactly as between two in-repo services.\n *\n * Drawn whatever `showExternalNodes` says: these are OUR services, not vendor systems, and hiding them\n * would hide calls the reviewed graph asserts exist.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction remoteServicesDot(\n graph: RuntimeGraph,\n hidden: Set<string>,\n model: GraphRenderModel,\n): string {\n const remotes = graph.remoteServices ?? {};\n const names = Object.keys(remotes).sort();\n if (names.length === 0) return '';\n\n let dot =\n '\\n // Remote services — ours, but outside this workspace (reviewed remoteServices).\\n';\n for (const name of names) {\n const remote = remotes[name];\n const id = new RemoteServiceNodeId().of(name);\n // The label is display text; the service name stays visible because it is what `uses` target.\n const title =\n remote.label === undefined\n ? dotValue(name)\n : `${dotValue(remote.label)}\\\\n${dotValue(name)}`;\n dot += model.node(\n id,\n ` \"${id}\" [shape=box, style=\"filled,rounded,bold\", fillcolor=\"${REMOTE_SERVICE_FILL}\", ` +\n `color=\"${REMOTE_SERVICE_BORDER}\", label=\"${title}\\\\n(remote service)\\\\n` +\n `Implements (${remote.implements.length})\"];\\n`,\n );\n }\n for (const name of names) {\n const id = new RemoteServiceNodeId().of(name);\n for (const call of remotes[name].calledBy) {\n if (hidden.has(call.from)) continue;\n const via = call.via.length > 1 ? `Uses (${call.via.length}) ▾` : labelList(call.via);\n const label = call.type === 'pubsub' ? `${via}\\\\nenqueue` : via;\n const style = call.type === 'pubsub' ? ', style=dashed' : '';\n dot += model.edge(\n call.from,\n id,\n ` \"${serviceNodeId(call.from)}\" -> \"${id}\" [label=\"${label}\", color=\"${REMOTE_SERVICE_BORDER}\"${style}];\\n`,\n );\n }\n }\n return dot;\n}\n\n/** Build the Graphviz DOT for the runtime service graph. */\n// webpieces-disable no-function-outside-class -- module entry point, matching the sibling builders here\nexport function generateRuntimeDot(\n graph: RuntimeGraph,\n title: string = 'WebPieces Runtime Architecture',\n options: RuntimeVizOptions = new RuntimeVizOptions(),\n): string {\n return generateRuntimeRenderModel(graph, title, options).fullDot;\n}\n\n/** Capture synthetic drawable nodes and edges at their original emission sites. */\n// webpieces-disable no-function-outside-class -- renderer entry point matching generateRuntimeDot\nexport function generateRuntimeRenderModel(\n graph: RuntimeGraph,\n title: string = 'WebPieces Runtime Architecture',\n options: RuntimeVizOptions = new RuntimeVizOptions(),\n): GraphRenderModel {\n const model = new GraphRenderModel();\n model.viewer = 'runtime';\n const adapter = new RuntimeViewerAdapter(graph, options.projects);\n let dot = 'digraph RuntimeArchitecture {\\n';\n dot += ' rankdir=TB;\\n';\n dot += ' node [shape=box, style=\"filled,rounded\", fontname=\"Arial\"];\\n';\n dot += ' edge [fontname=\"Arial\", fontsize=12];\\n\\n';\n\n model.header = dot;\n\n // Services tagged drawOnGraph:false stay in the JSON but are omitted here —\n // both their node and any edge touching them are dropped from the render.\n const hidden = new Set(\n Object.keys(graph.services).filter(\n (name: string) => graph.services[name].drawOnGraph === false,\n ),\n );\n\n for (const name of Object.keys(graph.services)) {\n if (hidden.has(name)) continue;\n const svc = graph.services[name];\n dot += adapter.service(model, name, svc);\n }\n\n dot += '\\n';\n\n for (const edge of graph.runtimeEdges) {\n if (hidden.has(edge.from) || hidden.has(edge.to)) continue;\n // Queued hops are merged across methods, so they cannot be emitted one edge at a time.\n if (edge.type === 'pubsub') continue;\n dot += model.edge(edge.from, edge.to, edgeDot(edge));\n }\n\n dot += queuesDot(graph, hidden, model, options);\n dot += remoteServicesDot(graph, hidden, model);\n dot += triggerDot(graph, hidden, model);\n\n if (options.showExternalNodes) {\n dot += externalSystemsDot(graph, hidden, model);\n dot += externalDot(graph, hidden, model);\n }\n\n model.bands = adapter.levelBands(model);\n const footerStart = dot.length;\n dot += '\\n labelloc=\"t\";\\n';\n dot += ` label=\"${dotValue(title)}\\\\n(from ${dotValue(options.source())})\";\\n`;\n dot += ' fontsize=20;\\n';\n dot += '}\\n';\n // Nothing downstream parses this DOT until a human opens the page, so parse-shape is checked\n // HERE — a graph that cannot render is a generation failure, not a blank page to discover later.\n model.footer = dot.slice(footerStart);\n model.completeEndpoints();\n // #1179: a queue, trigger or external system belongs to the products of the services it touches.\n model.attachProducts((id: string): boolean => graph.services[id] !== undefined);\n adapter.contextModes(model);\n model.fullDot = new RuntimeLayout().render(model);\n assertValidDot(model.fullDot, 'runtime-architecture.dot');\n return model;\n}\n\nexport interface RuntimeVisualizationPaths {\n dotPath: string;\n htmlPath: string;\n}\n\n/**\n * Write the DOT + HTML renderings to tmp/webpieces/: `runtime-architecture.*` for the current graph,\n * `runtime-architecture-goal.*` for the goal (#1239), so neither page ever overwrites the other.\n * `comparison` is the Goal vs Current overlay the page carries (or why it is unavailable).\n */\nexport function writeRuntimeVisualization(\n graph: RuntimeGraph,\n workspaceRoot: string,\n title: string,\n options: RuntimeVizOptions,\n comparison: RuntimeComparisonView,\n): RuntimeVisualizationPaths {\n const outputDir = path.join(workspaceRoot, 'tmp', 'webpieces');\n if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });\n\n const model = generateRuntimeRenderModel(graph, title, options);\n model.comparison = comparison;\n const dot = model.fullDot;\n const base = options.page === RuntimePageKind.GOAL ? 'runtime-architecture-goal' : 'runtime-architecture';\n const dotPath = path.join(outputDir, `${base}.dot`);\n fs.writeFileSync(dotPath, dot, 'utf-8');\n\n const htmlPath = path.join(outputDir, `${base}.html`);\n fs.writeFileSync(htmlPath, new RuntimeHtmlPage().render(model, title, graph, options), 'utf-8');\n\n return { dotPath, htmlPath };\n}\n"]}
1
+ {"version":3,"file":"runtime-visualizer.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-visualizer.ts"],"names":[],"mappings":";;;AAqlBA,gDAMC;AAID,gEA+DC;AAWD,8DAqBC;;AA9rBD,qDAAiD;AACjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,+CAAyB;AACzB,mDAA6B;AAQ7B,6CAAqE;AACrE,6DAAwD;AACxD,qEAAgE;AAChE,2DAAsD;AACtD,2EAAsE;AACtE,qEAA+D;AAC/D,2DAc6B;AAE7B,+FAA+F;AAC/F,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAE9B,iGAAiG;AACjG,MAAM,QAAQ,GAAG,GAAG,CAAC;AAErB,+DAA0D;AAE1D,6DAA2E;AAAlE,wHAAA,iBAAiB,OAAA;AAAE,sHAAA,eAAe,OAAA;AAE3C;;;;;;GAMG;AACH,SAAS,YAAY,CAAC,IAAY;IAC9B,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9D,CAAC;AAED,gGAAgG;AAChG,oGAAoG;AACpG,SAAS,aAAa,CAAC,IAAY;IAC/B,OAAO,IAAA,qBAAQ,EAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,2FAA2F;AAC3F,uGAAuG;AACvG,SAAS,SAAS,CAAC,OAAiB;IAChC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,KAAa,EAAE,EAAE,CAAC,IAAA,qBAAQ,EAAC,KAAK,CAAC,CAAC,CAAC;IAC7D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,mBAAmB,EAAE,CAAC;QACxD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,mBAAmB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;;;;;GASG;AACH,wGAAwG;AACxG,SAAS,OAAO,CAAC,IAAiB;IAC9B,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,EAAE,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClC,MAAM,MAAM,GACR,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC;QACf,CAAC,CAAC,SAAS,IAAI,CAAC,GAAG,CAAC,MAAM,KAAK;QAC/B,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClE,MAAM,GAAG,GAAG,IAAI,CAAC,cAAc,KAAK,IAAI,CAAC;IACzC,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,IAAA,qBAAQ,EAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAA,qBAAQ,EAAC,MAAM,CAAC,CAAC;IAC5E,OAAO,MAAM,IAAI,SAAS,EAAE,aAAa,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,wCAAoB,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;AAC5F,CAAC;AAED,sGAAsG;AACtG,MAAM,cAAc;IACP,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;CAC1C;AAED;;;;GAIG;AACH,MAAM,UAAU;IAEQ;IACA;IACA;IAHpB,YACoB,OAAiB,EACjB,SAAmB,EACnB,SAAmB;QAFnB,YAAO,GAAP,OAAO,CAAU;QACjB,cAAS,GAAT,SAAS,CAAU;QACnB,cAAS,GAAT,SAAS,CAAU;IACpC,CAAC;CACP;AAED;;;;;;;;GAQG;AACH,uGAAuG;AACvG,SAAS,SAAS,CAAC,GAAW,EAAE,KAA+B;IAC3D,MAAM,IAAI,GAAG,IAAA,wBAAW,EAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,KAAK,CAAC,SAAS,KAAK,GAAG,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,MAAM,EAAE;QAAE,OAAO,IAAI,CAAC;IACpE,OAAO,GAAG,IAAI,aAAa,IAAA,wBAAW,EAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;AAC9D,CAAC;AAED,iIAAiI;AACjI,wGAAwG;AACxG,SAAS,WAAW,CAChB,EAAU,EACV,IAAY,EACZ,SAAmB,EACnB,SAAmB,EACnB,KAAuB,EACvB,SAAmB,EACnB,KAAa;IAEb,4FAA4F;IAC5F,uFAAuF;IACvF,2BAA2B;IAC3B,IAAI,GAAG,GAAG,KAAK,CAAC,IAAI,CAChB,EAAE,EACF,MAAM,EAAE,YAAY,+BAAW,gCAAgC,8BAAU,KAAK;QAC1E,UAAU,+BAAW,aAAa,sCAAkB,IAAI,KAAK,KAAK,IAAI,OAAO,EACjF,EAAE,EACF,SAAS,CACZ,CAAC;IACF,KAAK,MAAM,QAAQ,IAAI,SAAS;QAC5B,GAAG,IAAI,KAAK,CAAC,IAAI,CACb,QAAQ,EACR,EAAE,EACF,MAAM,aAAa,CAAC,QAAQ,CAAC,SAAS,EAAE,sCAAsC,CACjF,CAAC;IACN,KAAK,MAAM,QAAQ,IAAI,SAAS;QAC5B,GAAG,IAAI,KAAK,CAAC,IAAI,CACb,EAAE,EACF,QAAQ,EACR,MAAM,EAAE,SAAS,aAAa,CAAC,QAAQ,CAAC,qBAAqB,CAChE,CAAC;IACN,OAAO,GAAG,CAAC;AACf,CAAC;AAED;;;;;;;;;GASG;AACH,wGAAwG;AACxG,SAAS,WAAW,CAChB,OAAoC,EACpC,KAAmB,EACnB,IAA4B;IAE5B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC7C,8FAA8F;IAC9F,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAC3C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC;QAC/B,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7C,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACxD,MAAM,QAAQ,GAAG;YACb,GAAG;YACH,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK;YACvB,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;YACnB,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;YACnB,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;SAClB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACjB,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACtC,IAAI,QAAQ,KAAK,SAAS;YACtB,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,UAAU,CAAC,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;;YACjE,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;AAChC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,wGAAwG;AACxG,SAAS,SAAS,CACd,KAAmB,EACnB,MAAmB,EACnB,KAAuB,EACvB,OAA0B;IAE1B,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,CACpC,CAAC,CAAc,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CACtF,CAAC;IACF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEnC,IAAI,GAAG,GACH,2FAA2F;QAC3F,4EAA4E,CAAC;IAEjF,2FAA2F;IAC3F,MAAM,UAAU,GAAG,IAAI,GAAG,EAA0B,CAAC;IACrD,MAAM,OAAO,GAAG,IAAI,GAAG,EAA0B,CAAC;IAClD,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC;QAClE,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACrB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,cAAc,EAAE,CAAC,CAAC;YAClE,SAAS,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7C,SAAS,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC/C,CAAC;IACL,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAC9C,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC;QAClC,GAAG,IAAI,WAAW,CACd,UAAU,KAAK,CAAC,GAAG,CAAC,EAAE,EACtB,GAAG,IAAA,wBAAW,EAAC,YAAY,CAAC,GAAG,CAAC,CAAC,QAAQ,EACzC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAC1B,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAC1B,KAAK,EACL,CAAC,SAAS,GAAG,EAAE,CAAC,EAChB,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,KAAK,CAClC,CAAC;IACN,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;QACtE,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO;aACrB,GAAG,CAAC,CAAC,GAAW,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;aACvD,IAAI,CAAC,KAAK,CAAC,CAAC;QACjB,GAAG,IAAI,WAAW,CACd,UAAU,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,EACnC,IAAI,EACJ,KAAK,CAAC,SAAS,EACf,KAAK,CAAC,SAAS,EACf,KAAK,EACL,KAAK,CAAC,OAAO,EACb,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CACvC,CAAC;IACN,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,qFAAqF;AACrF,oGAAoG;AACpG,SAAS,KAAK,CAAC,GAAW;IACtB,OAAO,GAAG,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;;GAOG;AACH,wGAAwG;AACxG,SAAS,qBAAqB,CAC1B,QAAgB,EAChB,IAAY,EACZ,KAAa,EACb,QAAgB,EAChB,KAAuB;IAEvB,MAAM,KAAK,GAAG,mCAAe,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC;IAC7C,MAAM,IAAI,GAAG,kCAAc,CAAC,IAAI,CAAC,IAAI,iCAAa,CAAC;IACnD,gGAAgG;IAChG,6DAA6D;IAC7D,MAAM,OAAO,GAAG,KAAK,KAAK,+BAAW,CAAC;IACtC,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,sCAAkB,CAAC,CAAC,CAAC,EAAE,CAAC;IACjD,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,IAAA,wBAAW,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAA,qBAAQ,EAAC,KAAK,CAAC,CAAC;IAC5D,6FAA6F;IAC7F,uEAAuE;IACvE,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,UAAU,+BAAW,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IACzD,OAAO,KAAK,CAAC,IAAI,CACb,WAAW,KAAK,CAAC,QAAQ,CAAC,EAAE,EAC5B,cAAc,KAAK,CAAC,QAAQ,CAAC,YAAY,KAAK,gCAAgC,IAAI,KAAK;QACnF,GAAG,MAAM,UAAU,MAAM,GAAG,IAAI,OAAO,QAAQ,QAAQ,CAC9D,CAAC;AACN,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wGAAwG;AACxG,SAAS,UAAU,CAAC,KAAmB,EAAE,MAAmB,EAAE,KAAuB;IACjF,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAiB,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACtF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAErC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,IAAI,GAAG,GACH,0FAA0F,CAAC;IAC/F,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAuB,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,EAAE,CAAC;QAC1F,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC/C,MAAM,KAAK,GAAG,IAAA,qBAAQ,EAAC,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAC3D,MAAM,EAAE,GAAG,SAAS,KAAK,CAAC,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;QAChE,MAAM,QAAQ,GAAG,IAAA,qBAAQ,EAAC,OAAO,CAAC,SAAS,IAAI,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACnF,GAAG;YACC,KAAK,CAAC,IAAI,CACN,EAAE,EACF,MAAM,EAAE,+CAA+C,6BAAS,KAAK;gBACjE,UAAU,+BAAW,yBAAyB,CACrD;gBACD,KAAK,CAAC,IAAI,CACN,EAAE,EACF,OAAO,CAAC,OAAO,EACf,MAAM,EAAE,SAAS,OAAO,aAAa,KAAK,MAAM,QAAQ,aAAa,+BAAW,OAAO,CAC1F,CAAC;IACV,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,IAAI,mDAAuB,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC;QAC/D,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACxC,MAAM,KAAK,GAAG,IAAA,qBAAQ,EAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC9B,6FAA6F;QAC7F,6FAA6F;QAC7F,kCAAkC;QAClC,MAAM,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YACnB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChB,GAAG;gBACC,MAAM,KAAK,SAAS;oBAChB,CAAC,CAAC,KAAK,CAAC,IAAI,CACN,EAAE,EACF,MAAM,EAAE,mDAAmD,iCAAa,KAAK;wBACzE,UAAU,mCAAe,aAAa,IAAA,qBAAQ,EAAC,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAC5F;oBACH,CAAC,CAAC,qBAAqB,CACjB,WAAW,MAAM,CAAC,KAAK,EAAE,EACzB,MAAM,CAAC,IAAI,EACX,MAAM,CAAC,KAAK,EACZ,iBAAiB,EACjB,KAAK,CACR,CAAC;QAChB,CAAC;QACD,GAAG,IAAI,KAAK,CAAC,IAAI,CACb,EAAE,EACF,OAAO,CAAC,OAAO,EACf,MAAM,EAAE,SAAS,OAAO,aAAa,KAAK,2BAA2B,mCAAe,OAAO,CAC9F,CAAC;IACN,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED;;;;;;;;;GASG;AACH,wGAAwG;AACxG,SAAS,kBAAkB,CACvB,KAAmB,EACnB,MAAmB,EACnB,KAAuB;IAEvB,MAAM,OAAO,GAAG,KAAK,CAAC,eAAe,IAAI,EAAE,CAAC;IAC5C,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IACxC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEhC,IAAI,GAAG,GACH,sFAAsF,CAAC;IAC3F,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACnB,MAAM,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;QAC3B,GAAG,IAAI,qBAAqB,CACxB,EAAE,EACF,MAAM,CAAC,IAAI,EACX,MAAM,CAAC,KAAK,EACZ,YAAY,IAAA,qBAAQ,EAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EACnC,KAAK,CACR,CAAC;IACN,CAAC;IACD,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACnB,MAAM,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;QAC3B,MAAM,GAAG,GACL,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;YACpB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,YAAY,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC;QACzH,KAAK,MAAM,OAAO,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YAC9C,IAAI,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC;gBAAE,SAAS;YAClC,GAAG,IAAI,KAAK,CAAC,IAAI,CACb,OAAO,EACP,WAAW,KAAK,CAAC,EAAE,CAAC,EAAE,EACtB,MAAM,aAAa,CAAC,OAAO,CAAC,iBAAiB,KAAK,CAAC,EAAE,CAAC,IAAI,GAAG,KAAK,CACrE,CAAC;QACN,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED;;;;;;;;;;;GAWG;AACH,wGAAwG;AACxG,SAAS,WAAW,CAAC,KAAmB,EAAE,MAAmB,EAAE,KAAuB;IAClF,sDAAsD;IACtD,MAAM,UAAU,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC/C,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC;QACrC,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,SAAS;QACtC,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,cAAc,KAAK,SAAS;YAAE,SAAS;QAChE,wFAAwF;QACxF,gFAAgF;QAChF,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC,GAAG,CAAC;QACvD,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,OAAO,GAAG,QAAQ,GAAG,QAAQ,EAAE,CAAC;QACnD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAClD,UAAU,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACvC,CAAC;IACD,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAErC,IAAI,GAAG,GAAG,qFAAqF,CAAC;IAChG,6FAA6F;IAC7F,2FAA2F;IAC3F,yBAAyB;IACzB,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/F,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAC3C,GAAG,IAAI,KAAK,CAAC,IAAI,CACb,aAAa,QAAQ,EAAE,EACvB,gBAAgB,IAAA,qBAAQ,EAAC,QAAQ,CAAC,mDAAmD,iCAAa,KAAK;YACnG,UAAU,mCAAe,aAAa,IAAA,qBAAQ,EAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CACjG,CAAC;IACN,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAC9C,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAClC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACzB,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAC1B,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,IAAI,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC1E,6FAA6F;QAC7F,kEAAkE;QAClE,GAAG,IAAI,KAAK,CAAC,IAAI,CACb,OAAO,EACP,aAAa,QAAQ,EAAE,EACvB,MAAM,aAAa,CAAC,OAAO,CAAC,mBAAmB,IAAA,qBAAQ,EAAC,QAAQ,CAAC,IAAI;YACjE,WAAW,GAAG,aAAa,mCAAe,OAAO,CACxD,CAAC;IACN,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,wGAAwG;AACxG,SAAS,iBAAiB,CACtB,KAAmB,EACnB,MAAmB,EACnB,KAAuB;IAEvB,MAAM,OAAO,GAAG,KAAK,CAAC,cAAc,IAAI,EAAE,CAAC;IAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAElC,IAAI,GAAG,GACH,wFAAwF,CAAC;IAC7F,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,EAAE,GAAG,IAAI,4CAAmB,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QAC9C,8FAA8F;QAC9F,MAAM,KAAK,GACP,MAAM,CAAC,KAAK,KAAK,SAAS;YACtB,CAAC,CAAC,IAAA,qBAAQ,EAAC,IAAI,CAAC;YAChB,CAAC,CAAC,GAAG,IAAA,qBAAQ,EAAC,MAAM,CAAC,KAAK,CAAC,MAAM,IAAA,qBAAQ,EAAC,IAAI,CAAC,EAAE,CAAC;QAC1D,GAAG,IAAI,KAAK,CAAC,IAAI,CACb,EAAE,EACF,MAAM,EAAE,yDAAyD,uCAAmB,KAAK;YACrF,UAAU,yCAAqB,aAAa,KAAK,wBAAwB;YACzE,eAAe,MAAM,CAAC,UAAU,CAAC,MAAM,QAAQ,CACtD,CAAC;IACN,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,IAAI,4CAAmB,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QAC9C,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;YACxC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,SAAS;YACpC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtF,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,GAAG,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC;YAChE,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7D,GAAG,IAAI,KAAK,CAAC,IAAI,CACb,IAAI,CAAC,IAAI,EACT,EAAE,EACF,MAAM,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,KAAK,aAAa,yCAAqB,IAAI,KAAK,MAAM,CAC/G,CAAC;QACN,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,4DAA4D;AAC5D,wGAAwG;AACxG,SAAgB,kBAAkB,CAC9B,KAAmB,EACnB,QAAgB,gCAAgC,EAChD,UAA6B,IAAI,uCAAiB,EAAE;IAEpD,OAAO,0BAA0B,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC;AACrE,CAAC;AAED,mFAAmF;AACnF,kGAAkG;AAClG,SAAgB,0BAA0B,CACtC,KAAmB,EACnB,QAAgB,gCAAgC,EAChD,UAA6B,IAAI,uCAAiB,EAAE;IAEpD,MAAM,KAAK,GAAG,IAAI,qCAAgB,EAAE,CAAC;IACrC,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC;IACzB,MAAM,OAAO,GAAG,IAAI,6CAAoB,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClE,IAAI,GAAG,GAAG,iCAAiC,CAAC;IAC5C,GAAG,IAAI,iBAAiB,CAAC;IACzB,GAAG,IAAI,iEAAiE,CAAC;IACzE,GAAG,IAAI,6CAA6C,CAAC;IAErD,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC;IAEnB,4EAA4E;IAC5E,0EAA0E;IAC1E,MAAM,MAAM,GAAG,IAAI,GAAG,CAClB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,CAC9B,CAAC,IAAY,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,KAAK,KAAK,CAC/D,CACJ,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7C,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QAC/B,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACjC,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;IAC7C,CAAC;IAED,GAAG,IAAI,IAAI,CAAC;IAEZ,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QACpC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAAE,SAAS;QAC3D,uFAAuF;QACvF,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;YAAE,SAAS;QACrC,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACzD,CAAC;IAED,GAAG,IAAI,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IAChD,GAAG,IAAI,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAC/C,GAAG,IAAI,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAExC,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;QAC5B,GAAG,IAAI,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;QAChD,GAAG,IAAI,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC;IAC/B,GAAG,IAAI,qBAAqB,CAAC;IAC7B,GAAG,IAAI,YAAY,IAAA,qBAAQ,EAAC,KAAK,CAAC,YAAY,IAAA,qBAAQ,EAAC,OAAO,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC;IAChF,GAAG,IAAI,kBAAkB,CAAC;IAC1B,GAAG,IAAI,KAAK,CAAC;IACb,6FAA6F;IAC7F,iGAAiG;IACjG,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACtC,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC1B,iGAAiG;IACjG,KAAK,CAAC,cAAc,CAAC,CAAC,EAAU,EAAW,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,SAAS,CAAC,CAAC;IAChF,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAC5B,KAAK,CAAC,OAAO,GAAG,IAAI,8BAAa,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClD,IAAA,2BAAc,EAAC,KAAK,CAAC,OAAO,EAAE,0BAA0B,CAAC,CAAC;IAC1D,OAAO,KAAK,CAAC;AACjB,CAAC;AAOD;;;GAGG;AACH,SAAgB,yBAAyB,CACrC,KAAmB,EACnB,aAAqB,EACrB,KAAa,EACb,OAA0B,EAC1B,UAAiC;IAEjC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;IAC/D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5E,MAAM,KAAK,GAAG,0BAA0B,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IAChE,KAAK,CAAC,UAAU,GAAG,UAAU,CAAC;IAC9B,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC;IAC1B,MAAM,IAAI,GAAG,sBAAsB,CAAC;IACpC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,MAAM,CAAC,CAAC;IACpD,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAExC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,OAAO,CAAC,CAAC;IACtD,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,mCAAe,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;IAEhG,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AACjC,CAAC","sourcesContent":["import { RuntimeLayout } from './runtime-layout';\n/**\n * Runtime Visualizer\n *\n * Renders the runtime microservice graph (services + inferred Z -> X edges,\n * each labeled with the api(s) they flow over) to DOT + interactive HTML in\n * tmp/webpieces/runtime-architecture.{dot,html}.\n *\n * Each service node names the contracts it IMPLEMENTS. That list used to be\n * collapsed into a server/client boolean and thrown away — leaving an api that a\n * server serves but nothing in-repo calls completely invisible, and making a\n * correct api design look like a detection failure. What a service USES is NOT\n * listed: every use already draws an outgoing arrow labeled with the same\n * contract, so repeating it in the box only made every box wider.\n *\n * Shape says what a node IS and line style says what a call IS. Solid = rpc: the\n * request follows the arrow and the response flows back. Dashed = event: it flows\n * in the arrow's direction and returns once it is queued. A queue is a sideways\n * cylinder, a datastore an upright one. A queue box lists ONE LINE PER QUEUE: the\n * unit underneath is still the METHOD (what Terraform creates, and what\n * runtime-dependencies.json records), but queues of one contract sharing the same\n * producers and consumers are drawn together instead of as adjacent near-identical\n * boxes — every queue is still named, only the node count drops.\n *\n * Calls that leave the repo (a contract NOTHING in-repo implements — firestore,\n * gmail, ...) are drawn as terminal nodes, so the vendor systems that actually\n * page you at 3am stop being missing from the picture. One that DECLARES what it\n * is, via an `@externalSystem` JSDoc tag or an `external:` nx tag, gets the shape\n * of the thing it is; the rest stay generic dashed boxes. They are RENDER-ONLY:\n * derivation, levels and cycle detection never see them.\n *\n * The same is true in the other direction for endpoints nothing in-repo CALLS: a\n * `cron` method hangs off a clock and an `external` method off a box naming the\n * CALLER that posts to it (`twilio`), separately from an outbound sink for the same\n * vendor, so callers can sit on the top row and sinks on L0. Those are the\n * entry points that wake a service up at 3am, and a graph built only from in-repo\n * callers cannot show them at all.\n *\n * EVERY node on the rendered page is clickable and opens the SHARED floating menu\n * (graph-node-menu.ts — one implementation, also used by architecture/dependencies.html\n * and every project's design.html). Lock/Unlock pins separate complete incoming/outgoing chains, with hover adding a temporary chain. There is no \"View Design\"\n * item here — a node is a running service, queue, datastore or third-party system, not\n * an nx project, so no design.html exists to point at and the item is absent rather than\n * dead. Filter Unconnected follows separate incoming/outgoing chains and compacts the rendered\n * topology; Turn off Filter restores the full picture. Lock remains independent of Filter.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport type {\n RuntimeGraph,\n RuntimeEdge,\n RuntimeQueue,\n RuntimeService,\n RuntimeTrigger,\n} from './runtime-graph';\nimport { dotValue, recordValue, assertValidDot } from './dot-syntax';\nimport { GraphRenderModel } from './graph-render-model';\nimport { RuntimeViewerAdapter } from './runtime-viewer-adapter';\nimport { RuntimeHtmlPage } from './runtime-html-page';\nimport { RuntimeExternalTriggers } from './runtime-external-triggers';\nimport { RemoteServiceNodeId } from './remote-service-node-id';\nimport {\n QUEUE_FILL,\n QUEUE_SHAPE,\n QUEUE_LABEL_PREFIX,\n QUEUE_CLASS,\n EXTERNAL_SHAPES,\n EXTERNAL_FILLS,\n EXTERNAL_FILL,\n EXTERNAL_BORDER,\n CRON_FILL,\n CRON_BORDER,\n CUT_CYCLE_EDGE_ATTRS,\n REMOTE_SERVICE_FILL,\n REMOTE_SERVICE_BORDER,\n} from './runtime-viz-theme';\n\n/** Apis per line inside a node label — beyond this the box grows wider than it is readable. */\nconst APIS_PER_LABEL_LINE = 3;\n\n/** Separator for the (service, external-library) grouping key; illegal in both project names. */\nconst PAIR_SEP = '|';\n\nimport { RuntimeVizOptions } from './runtime-viz-options';\nimport type { RuntimeComparisonView } from './graph-render-model';\nexport { RuntimeVizOptions, RuntimePageKind } from './runtime-viz-options';\n\n/**\n * The DISPLAY name of a project/service: scope stripped. LABELS ONLY.\n *\n * Never an identity. `@scope/public-api` and `public-api` are two distinct projects that strip to\n * the same string, and keying nodes or edges on that fuses them into one box (and draws the edge\n * between them as a self-loop). See {@link serviceNodeId} and lib/graph-names.ts.\n */\nfunction getShortName(name: string): string {\n return name.includes('/') ? name.split('/').pop()! : name;\n}\n\n/** The DOT identity of a runtime service: its project key, escaped — never the display name. */\n// webpieces-disable no-function-outside-class -- DOT id builder, matching getShortName in this file\nfunction serviceNodeId(name: string): string {\n return dotValue(name);\n}\n\n/** Chunk a list into `\\n`-separated label lines of at most APIS_PER_LABEL_LINE entries. */\n// webpieces-disable no-function-outside-class -- DOT label builder, matching getShortName in this file\nfunction labelList(entries: string[]): string {\n const lines: string[] = [];\n const safe = entries.map((entry: string) => dotValue(entry));\n for (let i = 0; i < safe.length; i += APIS_PER_LABEL_LINE) {\n lines.push(safe.slice(i, i + APIS_PER_LABEL_LINE).join(', '));\n }\n return lines.join('\\\\n');\n}\n\n/**\n * DOT for ONE NON-QUEUED runtime edge: a direct labeled SOLID arrow (producer calls consumer, the\n * response comes back). Queued (pubsub) hops are drawn by {@link queuesDot} instead, because they\n * are merged across methods and therefore cannot be emitted one edge at a time.\n *\n * Dashed is the graph's one line-level distinction, and it now says one of two things: an event that\n * returns as soon as it is queued, or (in red, labelled \"legacy cycle\") an edge a `cutLegacyCycle:`\n * tag holds out of leveling. A cut edge is still DRAWN — erasing the arrow would erase exactly the\n * debt the tag records — it is only drawn as the debt it is. See lib/cut-legacy-cycle-resolver.ts.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction edgeDot(edge: RuntimeEdge): string {\n const from = serviceNodeId(edge.from);\n const to = serviceNodeId(edge.to);\n const viaRaw =\n edge.via.length > 1\n ? `Uses (${edge.via.length}) ▾`\n : edge.via.map((v: string) => getShortName(v)).join(', ');\n const cut = edge.cutLegacyCycle === true;\n const label = cut ? `${dotValue(viaRaw)}\\\\nlegacy cycle` : dotValue(viaRaw);\n return ` \"${from}\" -> \"${to}\" [label=\"${label}\"${cut ? CUT_CYCLE_EDGE_ATTRS : ''}];\\n`;\n}\n\n/** The VISIBLE producers and consumers of one queue, gathered from the edges that survived hiding. */\nclass QueueEndpoints {\n readonly producers = new Set<string>();\n readonly consumers = new Set<string>();\n}\n\n/**\n * One drawn queue BOX: the queue keys (`Api.method`) it shows, and the endpoints they all share.\n * Several keys land in one box only when they agree on contract, producers, consumers and\n * their persisted level — see {@link queuesDot}.\n */\nclass QueueGroup {\n constructor(\n public readonly members: string[],\n public readonly producers: string[],\n public readonly consumers: string[],\n ) {}\n}\n\n/**\n * The label line for ONE queue inside a box: `Api.method`, and the Terraform queue name ONLY when it\n * is not the derived `${Api}-${method}`.\n *\n * Printing a derived name was pure restatement — the line above it already said `Api.method`, so\n * every queue box carried a second line that added nothing and doubled its height. A `@Queue(...)`\n * OVERRIDE is the opposite: that string appears nowhere else on the graph, and it is the one string\n * Terraform must match, so it stays.\n */\n// webpieces-disable no-function-outside-class -- DOT label builder, matching getShortName in this file\nfunction queueLine(key: string, queue: RuntimeQueue | undefined): string {\n const line = recordValue(key);\n if (queue === undefined) return line;\n if (queue.queueName === `${queue.api}-${queue.method}`) return line;\n return `${line}\\\\nqueue: ${recordValue(queue.queueName)}`;\n}\n\n/** The node statement + enqueue/deliver arrows for one queue box. Service identities remain raw; only emitted DOT is escaped. */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction queueBoxDot(\n id: string,\n body: string,\n producers: string[],\n consumers: string[],\n model: GraphRenderModel,\n queueKeys: string[],\n level: number,\n): string {\n // Record-mode label: the text must clear recordValue(), and QUEUE_LABEL_PREFIX supplies the\n // empty leading field that draws the cylinder's end cap. Drop it and the node silently\n // degrades to a plain box.\n let dot = model.node(\n id,\n ` \"${id}\" [shape=${QUEUE_SHAPE}, style=\"filled\", fillcolor=\"${QUEUE_FILL}\", ` +\n `class=\"${QUEUE_CLASS}\", label=\"${QUEUE_LABEL_PREFIX}L${level} ${body}\"];\\n`,\n [],\n queueKeys,\n );\n for (const producer of producers)\n dot += model.edge(\n producer,\n id,\n ` \"${serviceNodeId(producer)}\" -> \"${id}\" [label=\"enqueue\", style=dashed];\\n`,\n );\n for (const consumer of consumers)\n dot += model.edge(\n id,\n consumer,\n ` \"${id}\" -> \"${serviceNodeId(consumer)}\" [style=dashed];\\n`,\n );\n return dot;\n}\n\n/**\n * Group the queued hops into boxes. The key is (contract, producer SET, consumer SET) — NOT the\n * (from,to) pair of some edge. Persisted levels must also match: hiding a foreign producer\n * can make a foreign queue look self-only without changing its generated level.\n *\n * A queue node converges EVERY producer and consumer of its method onto one box, so keying by a\n * service PAIR would re-split a two-producer queue into two boxes and invent topology nobody wrote.\n * Only queues of the same contract whose producer and consumer sets are IDENTICAL may share a box;\n * anything else stays separate, because merging it would assert a routing that does not exist.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction groupQueues(\n byQueue: Map<string, QueueEndpoints>,\n graph: RuntimeGraph,\n tags: Record<string, string>,\n): QueueGroup[] {\n const groups = new Map<string, QueueGroup>();\n // Sorted keys → the first member of every group, and therefore its node id, is deterministic.\n for (const key of [...byQueue.keys()].sort()) {\n const ends = byQueue.get(key)!;\n const producers = [...ends.producers].sort();\n const consumers = [...ends.consumers].sort();\n const api = graph.queues[key]?.api ?? key.split('.')[0];\n const mergeKey = [\n api,\n graph.queues[key].level,\n producers.join(','),\n consumers.join(','),\n tags[key] ?? '',\n ].join(PAIR_SEP);\n const existing = groups.get(mergeKey);\n if (existing === undefined)\n groups.set(mergeKey, new QueueGroup([key], producers, consumers));\n else existing.members.push(key);\n }\n return [...groups.values()];\n}\n\n/**\n * The queued half of the graph: producer → QUEUE → consumer, drawn with a sideways-cylinder node and\n * DASHED arrows (an event returns as soon as it is queued).\n *\n * Contract-only edges converge on one box per API across all producers and consumers.\n * For exact method declarations, one BOX may list several queues, one per line. The unit Cloud Tasks and Terraform create is still\n * the METHOD — that is what runtime-dependencies.json records and this render never changes it — but\n * two methods of one contract flowing between exactly the same producers and consumers drew two\n * adjacent boxes whose only difference was the method name, and each carried a `queue:` line that\n * merely restated it. Listing them inside ONE box keeps every queue named (you can still see which\n * one is stuck) at a fraction of the node count.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction queuesDot(\n graph: RuntimeGraph,\n hidden: Set<string>,\n model: GraphRenderModel,\n options: RuntimeVizOptions,\n): string {\n const queued = graph.runtimeEdges.filter(\n (e: RuntimeEdge) => e.type === 'pubsub' && !hidden.has(e.from) && !hidden.has(e.to),\n );\n if (queued.length === 0) return '';\n\n let dot =\n '\\n // Queued hops. Each LINE in a box is one Cloud Tasks queue; queues of one contract\\n' +\n ' // sharing the same producers AND consumers are drawn in a single box.\\n';\n\n // Contract-only declarations converge by API, independently of the enqueuer/consumer pair.\n const byContract = new Map<string, QueueEndpoints>();\n const byQueue = new Map<string, QueueEndpoints>();\n for (const edge of queued) {\n const keys = edge.queue === undefined ? edge.via : [edge.queue];\n const endpoints = edge.queue === undefined ? byContract : byQueue;\n for (const key of keys) {\n if (!endpoints.has(key)) endpoints.set(key, new QueueEndpoints());\n endpoints.get(key)!.producers.add(edge.from);\n endpoints.get(key)!.consumers.add(edge.to);\n }\n }\n for (const api of [...byContract.keys()].sort()) {\n const ends = byContract.get(api)!;\n dot += queueBoxDot(\n `queue__${dotId(api)}`,\n `${recordValue(getShortName(api))} queue`,\n [...ends.producers].sort(),\n [...ends.consumers].sort(),\n model,\n [`queue:${api}`],\n graph.contractQueues[api].level,\n );\n }\n\n for (const group of groupQueues(byQueue, graph, options.queueGroupTags)) {\n const body = group.members\n .map((key: string) => queueLine(key, graph.queues[key]))\n .join('\\\\n');\n dot += queueBoxDot(\n `queue__${dotId(group.members[0])}`,\n body,\n group.producers,\n group.consumers,\n model,\n group.members,\n graph.queues[group.members[0]].level,\n );\n }\n return dot;\n}\n\n/** A DOT-safe node-id fragment: anything but letters, digits and `_` becomes `_`. */\n// webpieces-disable no-function-outside-class -- DOT id builder, matching getShortName in this file\nfunction dotId(raw: string): string {\n return raw.replace(/[^A-Za-z0-9_]/g, '_');\n}\n\n/**\n * ONE external-system node statement: the shape/fill of what the system IS, its label, and a\n * parenthesised subtitle saying which way it faces.\n *\n * Shared by the OUTBOUND systems ({@link externalSystemsDot}) and the INBOUND callers\n * ({@link triggerDot}) on purpose. Callers use a separate identity from outbound sinks\n * because their generated levels differ, while sharing the same shape and color palette.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction externalSystemNodeDot(\n identity: string,\n kind: string,\n label: string,\n subtitle: string,\n model: GraphRenderModel,\n): string {\n const shape = EXTERNAL_SHAPES[kind] ?? 'box';\n const fill = EXTERNAL_FILLS[kind] ?? EXTERNAL_FILL;\n // An Mrecord-shaped system needs the same empty leading field as a queue node, or it renders as\n // a plain box and silently loses the sideways-cylinder read.\n const isQueue = shape === QUEUE_SHAPE;\n const prefix = isQueue ? QUEUE_LABEL_PREFIX : '';\n const text = isQueue ? recordValue(label) : dotValue(label);\n // Only a queue-kind system is marked: a database here is an UPRIGHT cylinder and must not be\n // caught by the browser-side reshaping that lays queues on their side.\n const marker = isQueue ? `class=\"${QUEUE_CLASS}\", ` : '';\n return model.node(\n `system__${dotId(identity)}`,\n ` \"system__${dotId(identity)}\" [shape=${shape}, style=\"filled\", fillcolor=\"${fill}\", ` +\n `${marker}label=\"${prefix}${text}\\\\n(${subtitle})\"];\\n`,\n );\n}\n\n/**\n * The clock and outside-system nodes for endpoints NOTHING in-repo calls.\n *\n * A cron sweep and a GCP push subscription are real runtime entry points with real Terraform behind\n * them, but they produce no runtime EDGE (there is no in-repo caller), so until now they were simply\n * absent — a server's most operationally interesting endpoint could be invisible on its own graph.\n * Both are drawn pointing INTO the service that serves them, the opposite direction from\n * {@link externalDot}'s outbound vendor calls.\n *\n * The inbound box names the CALLER (`twilio`), never the contract. Naming the contract was the whole\n * bug: it restated what the service box directly below already says, while the one fact the box\n * exists to convey — which vendor is posting to us — appeared nowhere. The contract keeps its place\n * on the EDGE label, where `WhatsAppApi.inbound` reads as \"…posts to this method\".\n *\n * Inbound identities are separate from outbound sinks so each has its own generated level.\n * Several methods called by the same vendor still share one caller box.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction triggerDot(graph: RuntimeGraph, hidden: Set<string>, model: GraphRenderModel): string {\n const triggers = graph.triggers.filter((t: RuntimeTrigger) => !hidden.has(t.service));\n if (triggers.length === 0) return '';\n\n const emitted = new Set<string>();\n let dot =\n '\\n // Entry points nothing in this repo calls: a clock, or a system outside the repo.\\n';\n for (const trigger of triggers.filter((trigger: RuntimeTrigger) => trigger.kind === 'cron')) {\n const service = serviceNodeId(trigger.service);\n const label = dotValue(`${trigger.api}.${trigger.method}`);\n const id = `cron__${dotId(`${trigger.api}_${trigger.method}`)}`;\n const schedule = dotValue(trigger.queueName ?? `${trigger.api}-${trigger.method}`);\n dot +=\n model.node(\n id,\n ` \"${id}\" [shape=circle, style=\"filled\", fillcolor=\"${CRON_FILL}\", ` +\n `color=\"${CRON_BORDER}\", label=\"⏰\\\\ncron\"];\\n`,\n ) +\n model.edge(\n id,\n trigger.service,\n ` \"${id}\" -> \"${service}\" [label=\"${label}\\\\n${schedule}\", color=\"${CRON_BORDER}\"];\\n`,\n );\n }\n for (const group of new RuntimeExternalTriggers(triggers).groups) {\n const trigger = group.triggers[0];\n const service = serviceNodeId(group.to);\n const label = dotValue(group.label());\n const caller = trigger.caller;\n // Undeclared: only possible for a graph generated BEFORE the caller was required (generation\n // now fails instead). DOTTED and question-marked, so \"we were never told who this is\" cannot\n // be mistaken for a named vendor.\n const id = group.from;\n if (!emitted.has(id)) {\n emitted.add(id);\n dot +=\n caller === undefined\n ? model.node(\n id,\n ` \"${id}\" [shape=box, style=\"dotted,filled\", fillcolor=\"${EXTERNAL_FILL}\", ` +\n `color=\"${EXTERNAL_BORDER}\", label=\"${dotValue(trigger.api)}\\\\n? unknown caller\"];\\n`,\n )\n : externalSystemNodeDot(\n `caller__${caller.label}`,\n caller.kind,\n caller.label,\n 'external caller',\n model,\n );\n }\n dot += model.edge(\n id,\n trigger.service,\n ` \"${id}\" -> \"${service}\" [label=\"${label}\", style=dashed, color=\"${EXTERNAL_BORDER}\"];\\n`,\n );\n }\n return dot;\n}\n\n/**\n * The DECLARED external systems — the ones that said what they are, so they get a shape that says\n * it: a cylinder for a database, a folder for a bucket. Everything undeclared falls through to\n * {@link externalDot}'s generic grey box, which is why adding this broke nothing existing.\n *\n * The arrows are SOLID. A call to firestore or postgres is synchronous — it returns a value — and\n * the graph's rule is that solid means \"response comes back\". Being outside the repo is carried by\n * the node's shape, never by the line style; conflating the two is what made a blocking database\n * read look like an event.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction externalSystemsDot(\n graph: RuntimeGraph,\n hidden: Set<string>,\n model: GraphRenderModel,\n): string {\n const systems = graph.externalSystems ?? {};\n const ids = Object.keys(systems).sort();\n if (ids.length === 0) return '';\n\n let dot =\n '\\n // Declared external systems — drawn with the shape of what they actually are.\\n';\n for (const id of ids) {\n const system = systems[id];\n dot += externalSystemNodeDot(\n id,\n system.kind,\n system.label,\n `external ${dotValue(system.kind)}`,\n model,\n );\n }\n for (const id of ids) {\n const system = systems[id];\n const via =\n system.apis.length === 0\n ? ''\n : ` [label=\"${system.apis.length > 1 ? `Uses (${system.apis.length}) ▾` : labelList([...system.apis].sort())}\"]`;\n for (const service of [...system.usedBy].sort()) {\n if (hidden.has(service)) continue;\n dot += model.edge(\n service,\n `system__${dotId(id)}`,\n ` \"${serviceNodeId(service)}\" -> \"system__${dotId(id)}\"${via};\\n`,\n );\n }\n }\n return dot;\n}\n\n/**\n * The dashed terminal nodes + edges for calls that LEAVE the repo. Built from `unresolvedUses` —\n * a contract used by a node and implemented by nobody in-repo — which the derivation already\n * computes and which was, until now, only ever printed as a warning.\n *\n * Grouped by the api-lib that owns the contracts, so a service reaching three firestore contracts\n * draws ONE `lib-firestore (external)` box rather than three. The unresolved uses carry their\n * terminal level in the generated graph; grouping changes presentation only.\n *\n * A contract carrying an `@externalSystem` declaration is skipped here — {@link externalSystemsDot}\n * has already drawn it with a real shape, and rendering it in both places would double the node.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction externalDot(graph: RuntimeGraph, hidden: Set<string>, model: GraphRenderModel): string {\n // \"service|externalName\" -> the apis flowing over it.\n const apisByPair = new Map<string, string[]>();\n for (const use of graph.unresolvedUses) {\n if (hidden.has(use.service)) continue;\n if (graph.apis[use.api]?.externalSystem !== undefined) continue;\n // Keyed on the OWNER's full name, never its short name: two scoped api-libs whose short\n // names match are two external systems, and fusing them draws one box for both.\n const external = graph.apis[use.api]?.owner ?? use.api;\n const key = `${use.service}${PAIR_SEP}${external}`;\n if (!apisByPair.has(key)) apisByPair.set(key, []);\n apisByPair.get(key)!.push(use.api);\n }\n if (apisByPair.size === 0) return '';\n\n let dot = '\\n // Systems outside this repo — no in-repo service implements these contracts.\\n';\n // The node ID is prefixed so an external library can never collide with a service, and built\n // from the owner's FULL name so two same-short-named owners stay two boxes; only the label\n // carries the bare name.\n const externals = new Set([...apisByPair.keys()].map((key: string) => key.split(PAIR_SEP)[1]));\n for (const external of [...externals].sort()) {\n dot += model.node(\n `external__${external}`,\n ` \"external__${dotValue(external)}\" [shape=box, style=\"dashed,filled\", fillcolor=\"${EXTERNAL_FILL}\", ` +\n `color=\"${EXTERNAL_BORDER}\", label=\"${dotValue(getShortName(external))}\\\\n(external)\"];\\n`,\n );\n }\n for (const key of [...apisByPair.keys()].sort()) {\n const parts = key.split(PAIR_SEP);\n const service = parts[0];\n const external = parts[1];\n const apis = apisByPair.get(key)!.sort();\n const via = apis.length > 1 ? `Uses (${apis.length}) ▾` : labelList(apis);\n // SOLID: this is a synchronous call that returns a value. Dashed is reserved for events, and\n // \"outside the repo\" is already said by the node's dashed border.\n dot += model.edge(\n service,\n `external__${external}`,\n ` \"${serviceNodeId(service)}\" -> \"external__${dotValue(external)}\" ` +\n `[label=\"${via}\", color=\"${EXTERNAL_BORDER}\"];\\n`,\n );\n }\n return dot;\n}\n\n/**\n * The reviewed out-of-workspace services (#1203): service-shaped nodes (rounded, teal, bold outline,\n * \"(remote service)\"), with an arrow from every workspace service that targets one. An rpc call is a\n * solid arrow and a queued call a dashed \"enqueue\" arrow, exactly as between two in-repo services.\n *\n * Drawn whatever `showExternalNodes` says: these are OUR services, not vendor systems, and hiding them\n * would hide calls the reviewed graph asserts exist.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction remoteServicesDot(\n graph: RuntimeGraph,\n hidden: Set<string>,\n model: GraphRenderModel,\n): string {\n const remotes = graph.remoteServices ?? {};\n const names = Object.keys(remotes).sort();\n if (names.length === 0) return '';\n\n let dot =\n '\\n // Remote services — ours, but outside this workspace (reviewed remoteServices).\\n';\n for (const name of names) {\n const remote = remotes[name];\n const id = new RemoteServiceNodeId().of(name);\n // The label is display text; the service name stays visible because it is what `uses` target.\n const title =\n remote.label === undefined\n ? dotValue(name)\n : `${dotValue(remote.label)}\\\\n${dotValue(name)}`;\n dot += model.node(\n id,\n ` \"${id}\" [shape=box, style=\"filled,rounded,bold\", fillcolor=\"${REMOTE_SERVICE_FILL}\", ` +\n `color=\"${REMOTE_SERVICE_BORDER}\", label=\"${title}\\\\n(remote service)\\\\n` +\n `Implements (${remote.implements.length})\"];\\n`,\n );\n }\n for (const name of names) {\n const id = new RemoteServiceNodeId().of(name);\n for (const call of remotes[name].calledBy) {\n if (hidden.has(call.from)) continue;\n const via = call.via.length > 1 ? `Uses (${call.via.length}) ▾` : labelList(call.via);\n const label = call.type === 'pubsub' ? `${via}\\\\nenqueue` : via;\n const style = call.type === 'pubsub' ? ', style=dashed' : '';\n dot += model.edge(\n call.from,\n id,\n ` \"${serviceNodeId(call.from)}\" -> \"${id}\" [label=\"${label}\", color=\"${REMOTE_SERVICE_BORDER}\"${style}];\\n`,\n );\n }\n }\n return dot;\n}\n\n/** Build the Graphviz DOT for the runtime service graph. */\n// webpieces-disable no-function-outside-class -- module entry point, matching the sibling builders here\nexport function generateRuntimeDot(\n graph: RuntimeGraph,\n title: string = 'WebPieces Runtime Architecture',\n options: RuntimeVizOptions = new RuntimeVizOptions(),\n): string {\n return generateRuntimeRenderModel(graph, title, options).fullDot;\n}\n\n/** Capture synthetic drawable nodes and edges at their original emission sites. */\n// webpieces-disable no-function-outside-class -- renderer entry point matching generateRuntimeDot\nexport function generateRuntimeRenderModel(\n graph: RuntimeGraph,\n title: string = 'WebPieces Runtime Architecture',\n options: RuntimeVizOptions = new RuntimeVizOptions(),\n): GraphRenderModel {\n const model = new GraphRenderModel();\n model.viewer = 'runtime';\n const adapter = new RuntimeViewerAdapter(graph, options.projects);\n let dot = 'digraph RuntimeArchitecture {\\n';\n dot += ' rankdir=TB;\\n';\n dot += ' node [shape=box, style=\"filled,rounded\", fontname=\"Arial\"];\\n';\n dot += ' edge [fontname=\"Arial\", fontsize=12];\\n\\n';\n\n model.header = dot;\n\n // Services tagged drawOnGraph:false stay in the JSON but are omitted here —\n // both their node and any edge touching them are dropped from the render.\n const hidden = new Set(\n Object.keys(graph.services).filter(\n (name: string) => graph.services[name].drawOnGraph === false,\n ),\n );\n\n for (const name of Object.keys(graph.services)) {\n if (hidden.has(name)) continue;\n const svc = graph.services[name];\n dot += adapter.service(model, name, svc);\n }\n\n dot += '\\n';\n\n for (const edge of graph.runtimeEdges) {\n if (hidden.has(edge.from) || hidden.has(edge.to)) continue;\n // Queued hops are merged across methods, so they cannot be emitted one edge at a time.\n if (edge.type === 'pubsub') continue;\n dot += model.edge(edge.from, edge.to, edgeDot(edge));\n }\n\n dot += queuesDot(graph, hidden, model, options);\n dot += remoteServicesDot(graph, hidden, model);\n dot += triggerDot(graph, hidden, model);\n\n if (options.showExternalNodes) {\n dot += externalSystemsDot(graph, hidden, model);\n dot += externalDot(graph, hidden, model);\n }\n\n model.bands = adapter.levelBands(model);\n const footerStart = dot.length;\n dot += '\\n labelloc=\"t\";\\n';\n dot += ` label=\"${dotValue(title)}\\\\n(from ${dotValue(options.source())})\";\\n`;\n dot += ' fontsize=20;\\n';\n dot += '}\\n';\n // Nothing downstream parses this DOT until a human opens the page, so parse-shape is checked\n // HERE — a graph that cannot render is a generation failure, not a blank page to discover later.\n model.footer = dot.slice(footerStart);\n model.completeEndpoints();\n // #1179: a queue, trigger or external system belongs to the products of the services it touches.\n model.attachProducts((id: string): boolean => graph.services[id] !== undefined);\n adapter.contextModes(model);\n model.fullDot = new RuntimeLayout().render(model);\n assertValidDot(model.fullDot, 'runtime-architecture.dot');\n return model;\n}\n\nexport interface RuntimeVisualizationPaths {\n dotPath: string;\n htmlPath: string;\n}\n\n/**\n * Write the DOT + HTML renderings to tmp/webpieces/runtime-architecture.*. `comparison` is the\n * Goal vs Current overlay the page carries (or why it is unavailable, #1239).\n */\nexport function writeRuntimeVisualization(\n graph: RuntimeGraph,\n workspaceRoot: string,\n title: string,\n options: RuntimeVizOptions,\n comparison: RuntimeComparisonView,\n): RuntimeVisualizationPaths {\n const outputDir = path.join(workspaceRoot, 'tmp', 'webpieces');\n if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });\n\n const model = generateRuntimeRenderModel(graph, title, options);\n model.comparison = comparison;\n const dot = model.fullDot;\n const base = 'runtime-architecture';\n const dotPath = path.join(outputDir, `${base}.dot`);\n fs.writeFileSync(dotPath, dot, 'utf-8');\n\n const htmlPath = path.join(outputDir, `${base}.html`);\n fs.writeFileSync(htmlPath, new RuntimeHtmlPage().render(model, title, graph, options), 'utf-8');\n\n return { dotPath, htmlPath };\n}\n"]}
@@ -1,14 +1,12 @@
1
1
  import type { EnhancedGraph } from './graph-sorter';
2
2
  /**
3
- * Which saved runtime file a page draws (#1239). It decides the output file names, the provenance
4
- * the page states, and the source named under the graph's title.
3
+ * Which saved runtime file(s) a drawing reads (#1239): the current graph, or the Goal vs Current
4
+ * overlay the runtime page carries. It decides the source named under the graph's title.
5
5
  */
6
6
  export declare enum RuntimePageKind {
7
7
  /** architecture/runtime-dependencies.json — generated from source. */
8
8
  CURRENT = "current",
9
- /** architecture/runtime-dependencies-goal.json — a hand-authored proposal. */
10
- GOAL = "goal",
11
- /** The Goal vs Current overlay carried inside either page. */
9
+ /** The Goal vs Current overlay carried inside the runtime page. */
12
10
  COMPARISON = "comparison"
13
11
  }
14
12
  /** Render options for the runtime graph. */
@@ -2,16 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RuntimeVizOptions = exports.RuntimePageKind = void 0;
4
4
  /**
5
- * Which saved runtime file a page draws (#1239). It decides the output file names, the provenance
6
- * the page states, and the source named under the graph's title.
5
+ * Which saved runtime file(s) a drawing reads (#1239): the current graph, or the Goal vs Current
6
+ * overlay the runtime page carries. It decides the source named under the graph's title.
7
7
  */
8
8
  var RuntimePageKind;
9
9
  (function (RuntimePageKind) {
10
10
  /** architecture/runtime-dependencies.json — generated from source. */
11
11
  RuntimePageKind["CURRENT"] = "current";
12
- /** architecture/runtime-dependencies-goal.json — a hand-authored proposal. */
13
- RuntimePageKind["GOAL"] = "goal";
14
- /** The Goal vs Current overlay carried inside either page. */
12
+ /** The Goal vs Current overlay carried inside the runtime page. */
15
13
  RuntimePageKind["COMPARISON"] = "comparison";
16
14
  })(RuntimePageKind || (exports.RuntimePageKind = RuntimePageKind = {}));
17
15
  /** Render options for the runtime graph. */
@@ -41,8 +39,6 @@ class RuntimeVizOptions {
41
39
  }
42
40
  /** The saved file(s) this page draws, named under the title. */
43
41
  source() {
44
- if (this.page === RuntimePageKind.GOAL)
45
- return 'architecture/runtime-dependencies-goal.json';
46
42
  if (this.page === RuntimePageKind.COMPARISON)
47
43
  return 'Current → Goal: architecture/runtime-dependencies.json → architecture/runtime-dependencies-goal.json';
48
44
  return 'architecture/runtime-dependencies.json';