@webpieces/nx-webpieces-rules 0.4.911 → 0.4.913

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 (85) hide show
  1. package/README.md +11 -0
  2. package/executors.json +5 -0
  3. package/package.json +7 -7
  4. package/src/executors/help/executor.js +2 -0
  5. package/src/executors/help/executor.js.map +1 -1
  6. package/src/executors/visualize-runtime/executor.d.ts +3 -1
  7. package/src/executors/visualize-runtime/executor.js +9 -3
  8. package/src/executors/visualize-runtime/executor.js.map +1 -1
  9. package/src/executors/visualize-runtime-goal/executor.d.ts +27 -0
  10. package/src/executors/visualize-runtime-goal/executor.js +98 -0
  11. package/src/executors/visualize-runtime-goal/executor.js.map +1 -0
  12. package/src/executors/visualize-runtime-goal/schema.json +13 -0
  13. package/src/index.d.ts +1 -1
  14. package/src/index.js +3 -1
  15. package/src/index.js.map +1 -1
  16. package/src/lib/graph-color-modes.d.ts +15 -1
  17. package/src/lib/graph-color-modes.js +19 -1
  18. package/src/lib/graph-color-modes.js.map +1 -1
  19. package/src/lib/graph-filter.client.d.ts +9 -2
  20. package/src/lib/graph-filter.client.js +15 -0
  21. package/src/lib/graph-filter.client.js.map +1 -1
  22. package/src/lib/graph-legend.js +1 -1
  23. package/src/lib/graph-legend.js.map +1 -1
  24. package/src/lib/graph-mode-state.client.d.ts +9 -2
  25. package/src/lib/graph-mode-state.client.js +17 -4
  26. package/src/lib/graph-mode-state.client.js.map +1 -1
  27. package/src/lib/graph-page-shell.d.ts +11 -4
  28. package/src/lib/graph-page-shell.js +11 -9
  29. package/src/lib/graph-page-shell.js.map +1 -1
  30. package/src/lib/graph-products.d.ts +22 -3
  31. package/src/lib/graph-products.js +63 -13
  32. package/src/lib/graph-products.js.map +1 -1
  33. package/src/lib/graph-render-model.d.ts +41 -0
  34. package/src/lib/graph-render-model.js +45 -1
  35. package/src/lib/graph-render-model.js.map +1 -1
  36. package/src/lib/graph-visualizer.client.d.ts +7 -1
  37. package/src/lib/graph-visualizer.client.js +37 -4
  38. package/src/lib/graph-visualizer.client.js.map +1 -1
  39. package/src/lib/graph-visualizer.js +1 -1
  40. package/src/lib/graph-visualizer.js.map +1 -1
  41. package/src/lib/runtime-goal-comparison.d.ts +55 -0
  42. package/src/lib/runtime-goal-comparison.js +209 -0
  43. package/src/lib/runtime-goal-comparison.js.map +1 -0
  44. package/src/lib/runtime-goal-details.d.ts +17 -0
  45. package/src/lib/runtime-goal-details.js +118 -0
  46. package/src/lib/runtime-goal-details.js.map +1 -0
  47. package/src/lib/runtime-goal-file.d.ts +72 -0
  48. package/src/lib/runtime-goal-file.js +201 -0
  49. package/src/lib/runtime-goal-file.js.map +1 -0
  50. package/src/lib/runtime-goal-styles.d.ts +10 -0
  51. package/src/lib/runtime-goal-styles.js +25 -0
  52. package/src/lib/runtime-goal-styles.js.map +1 -0
  53. package/src/lib/runtime-goal-validator.d.ts +39 -0
  54. package/src/lib/runtime-goal-validator.js +236 -0
  55. package/src/lib/runtime-goal-validator.js.map +1 -0
  56. package/src/lib/runtime-goal-view.d.ts +22 -0
  57. package/src/lib/runtime-goal-view.js +64 -0
  58. package/src/lib/runtime-goal-view.js.map +1 -0
  59. package/src/lib/runtime-graph-diff.d.ts +107 -0
  60. package/src/lib/runtime-graph-diff.js +346 -0
  61. package/src/lib/runtime-graph-diff.js.map +1 -0
  62. package/src/lib/runtime-html-page.d.ts +8 -0
  63. package/src/lib/runtime-html-page.js +39 -6
  64. package/src/lib/runtime-html-page.js.map +1 -1
  65. package/src/lib/runtime-layout.d.ts +2 -0
  66. package/src/lib/runtime-layout.js +4 -0
  67. package/src/lib/runtime-layout.js.map +1 -1
  68. package/src/lib/runtime-union-graph.d.ts +20 -0
  69. package/src/lib/runtime-union-graph.js +94 -0
  70. package/src/lib/runtime-union-graph.js.map +1 -0
  71. package/src/lib/runtime-viewer-adapter.js +7 -5
  72. package/src/lib/runtime-viewer-adapter.js.map +1 -1
  73. package/src/lib/runtime-visualizer.client.js +7 -3
  74. package/src/lib/runtime-visualizer.client.js.map +1 -1
  75. package/src/lib/runtime-visualizer.d.ts +8 -3
  76. package/src/lib/runtime-visualizer.js +18 -10
  77. package/src/lib/runtime-visualizer.js.map +1 -1
  78. package/src/lib/runtime-viz-options.d.ts +28 -1
  79. package/src/lib/runtime-viz-options.js +36 -2
  80. package/src/lib/runtime-viz-options.js.map +1 -1
  81. package/src/plugin.js +2 -2
  82. package/src/plugin.js.map +1 -1
  83. package/src/runtime-targets.d.ts +6 -0
  84. package/src/runtime-targets.js +17 -0
  85. package/src/runtime-targets.js.map +1 -1
@@ -0,0 +1,346 @@
1
+ "use strict";
2
+ /**
3
+ * Goal vs Current (#1239): a SEMANTIC diff of two saved RuntimeGraph files.
4
+ *
5
+ * Both graphs are flattened into FACTS keyed by stable underlying identities — a service's project
6
+ * key, a queue's `Api.method`, a contract queue's API, an external system's identity — never by a
7
+ * shortened display name or a renderer-generated edge id. A fact is a NODE (service, queue, contract
8
+ * queue, external system, remote service, external library, trigger), a RELATIONSHIP (rpc call,
9
+ * enqueue, delivery, external use, remote call, unresolved use) or a CONTRACT (an `apis` entry).
10
+ *
11
+ * Comparison policy:
12
+ * - Values are canonical strings. Set-like arrays are de-duplicated and sorted, so object key order
13
+ * and array order alone never produce a change.
14
+ * - Queue facts are compared per method / per contract BEFORE presentation grouping, and every
15
+ * pubsub edge is split into its enqueue and delivery relationships, so a merged queue box cannot
16
+ * hide a difference between its members.
17
+ * - Bookkeeping is ignored: `levelBecause` (explanatory text the level machinery regenerates),
18
+ * `wiringUses` / `wiringImplements` (reviewed provenance), and `dependsOn` (the BUILD graph, which
19
+ * this runtime comparison deliberately does not cover). `maxLevel` is reported in the summary.
20
+ * - A service's outgoing relationships are one of its properties, so a service whose calls changed
21
+ * is itself CHANGED and its details show the before/after call list.
22
+ */
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.RuntimeGraphDiff = exports.RuntimeFactSet = exports.RuntimeGraphDiffResult = exports.RuntimeDiffCounts = exports.RuntimeFactChange = exports.RuntimeFact = exports.RuntimeFactKind = exports.RuntimeDiffStatus = void 0;
25
+ var RuntimeDiffStatus;
26
+ (function (RuntimeDiffStatus) {
27
+ RuntimeDiffStatus["ADDED"] = "added";
28
+ RuntimeDiffStatus["REMOVED"] = "removed";
29
+ RuntimeDiffStatus["CHANGED"] = "changed";
30
+ RuntimeDiffStatus["UNCHANGED"] = "unchanged";
31
+ })(RuntimeDiffStatus || (exports.RuntimeDiffStatus = RuntimeDiffStatus = {}));
32
+ var RuntimeFactKind;
33
+ (function (RuntimeFactKind) {
34
+ RuntimeFactKind["NODE"] = "node";
35
+ RuntimeFactKind["RELATIONSHIP"] = "relationship";
36
+ RuntimeFactKind["CONTRACT"] = "contract";
37
+ })(RuntimeFactKind || (exports.RuntimeFactKind = RuntimeFactKind = {}));
38
+ /** One identity's canonical properties in one graph. */
39
+ class RuntimeFact {
40
+ id;
41
+ kind;
42
+ category;
43
+ label;
44
+ properties;
45
+ constructor(id, kind, category, label, properties) {
46
+ this.id = id;
47
+ this.kind = kind;
48
+ this.category = category;
49
+ this.label = label;
50
+ this.properties = properties;
51
+ }
52
+ }
53
+ exports.RuntimeFact = RuntimeFact;
54
+ /** One identity compared across Current → Goal. `before` is null when added, `after` when removed. */
55
+ class RuntimeFactChange {
56
+ id;
57
+ kind;
58
+ category;
59
+ label;
60
+ status;
61
+ before;
62
+ after;
63
+ changedFields;
64
+ constructor(id, kind, category, label, status, before, after, changedFields) {
65
+ this.id = id;
66
+ this.kind = kind;
67
+ this.category = category;
68
+ this.label = label;
69
+ this.status = status;
70
+ this.before = before;
71
+ this.after = after;
72
+ this.changedFields = changedFields;
73
+ }
74
+ /** Human lines for a details panel: the status, then `field: before → after` per changed field. */
75
+ lines() {
76
+ const head = `${this.label} — ${this.status}`;
77
+ if (this.status === RuntimeDiffStatus.ADDED)
78
+ return [head, ...this.listing(this.after)];
79
+ if (this.status === RuntimeDiffStatus.REMOVED)
80
+ return [head, ...this.listing(this.before)];
81
+ if (this.status === RuntimeDiffStatus.UNCHANGED)
82
+ return [head];
83
+ return [head, ...this.changedFields.map((field) => ` ${field}: ${this.before[field] ?? '(absent)'} → ${this.after[field] ?? '(absent)'}`)];
84
+ }
85
+ listing(properties) {
86
+ return Object.keys(properties).map((field) => ` ${field}: ${properties[field]}`);
87
+ }
88
+ }
89
+ exports.RuntimeFactChange = RuntimeFactChange;
90
+ class RuntimeDiffCounts {
91
+ added;
92
+ removed;
93
+ changed;
94
+ unchanged;
95
+ constructor(added, removed, changed, unchanged) {
96
+ this.added = added;
97
+ this.removed = removed;
98
+ this.changed = changed;
99
+ this.unchanged = unchanged;
100
+ }
101
+ }
102
+ exports.RuntimeDiffCounts = RuntimeDiffCounts;
103
+ /** Every compared identity, sorted by id, plus graph-level bookkeeping. */
104
+ class RuntimeGraphDiffResult {
105
+ changes;
106
+ maxLevelBefore;
107
+ maxLevelAfter;
108
+ byId = new Map();
109
+ constructor(changes, maxLevelBefore, maxLevelAfter) {
110
+ this.changes = changes;
111
+ this.maxLevelBefore = maxLevelBefore;
112
+ this.maxLevelAfter = maxLevelAfter;
113
+ for (const change of changes)
114
+ this.byId.set(change.id, change);
115
+ }
116
+ get(id) {
117
+ return this.byId.get(id);
118
+ }
119
+ counts() {
120
+ const count = (status) => this.changes.filter((change) => change.status === status).length;
121
+ return new RuntimeDiffCounts(count(RuntimeDiffStatus.ADDED), count(RuntimeDiffStatus.REMOVED), count(RuntimeDiffStatus.CHANGED), count(RuntimeDiffStatus.UNCHANGED));
122
+ }
123
+ identical() {
124
+ return this.changes.every((change) => change.status === RuntimeDiffStatus.UNCHANGED);
125
+ }
126
+ /** The changed identities only (added, removed, changed), in id order. */
127
+ differences() {
128
+ return this.changes.filter((change) => change.status !== RuntimeDiffStatus.UNCHANGED);
129
+ }
130
+ }
131
+ exports.RuntimeGraphDiffResult = RuntimeGraphDiffResult;
132
+ /** One vendor library's unresolved uses, gathered before they become a fact. */
133
+ class ExternalLibraryFacts {
134
+ level;
135
+ apis = [];
136
+ usedBy = [];
137
+ constructor(level) {
138
+ this.level = level;
139
+ }
140
+ }
141
+ /** Flattens one RuntimeGraph into its facts. */
142
+ class RuntimeFactSet {
143
+ graph;
144
+ facts = new Map();
145
+ constructor(graph) {
146
+ this.graph = graph;
147
+ this.relationships();
148
+ this.services();
149
+ this.queues();
150
+ this.externals();
151
+ this.remotes();
152
+ this.triggers();
153
+ this.contracts();
154
+ }
155
+ add(fact) {
156
+ const existing = this.facts.get(fact.id);
157
+ if (existing === undefined) {
158
+ this.facts.set(fact.id, fact);
159
+ return;
160
+ }
161
+ // Duplicate relationship facts (two rpc edges between one pair) fold into one, union of values.
162
+ const merged = { ...existing.properties };
163
+ for (const [key, value] of Object.entries(fact.properties))
164
+ merged[key] = this.set([...this.split(merged[key]), ...this.split(value)]);
165
+ this.facts.set(fact.id, new RuntimeFact(fact.id, fact.kind, fact.category, fact.label, merged));
166
+ }
167
+ relationships() {
168
+ for (const edge of this.graph.runtimeEdges) {
169
+ if (edge.type !== 'pubsub') {
170
+ const props = { via: this.set(edge.via) };
171
+ if (edge.cutLegacyCycle === true)
172
+ props['cutLegacyCycle'] = 'true';
173
+ this.add(this.relation(`rpc:${edge.from}->${edge.to}`, 'rpc call', `RPC ${edge.from} → ${edge.to}`, props));
174
+ continue;
175
+ }
176
+ for (const queue of this.queueIds(edge)) {
177
+ this.add(this.relation(`enqueue:${edge.from}->${queue}`, 'enqueue', `Enqueue ${edge.from} → ${queue}`, {}));
178
+ this.add(this.relation(`deliver:${queue}->${edge.to}`, 'delivery', `Deliver ${queue} → ${edge.to}`, {}));
179
+ }
180
+ }
181
+ for (const [id, system] of Object.entries(this.graph.externalSystems ?? {})) {
182
+ for (const user of system.usedBy)
183
+ this.add(this.relation(`external-use:${user}->external-system:${id}`, 'external use', `Uses ${user} → ${system.label}`, {}));
184
+ }
185
+ for (const [name, remote] of Object.entries(this.graph.remoteServices ?? {})) {
186
+ for (const call of remote.calledBy)
187
+ this.add(this.relation(`remote-call:${call.from}->remote-service:${name}#${call.type}`, 'remote call', `Remote ${call.type} ${call.from} → ${name}`, { via: this.set(call.via) }));
188
+ }
189
+ for (const use of this.graph.unresolvedUses) {
190
+ const owner = this.owner(use.api);
191
+ this.add(this.relation(`unresolved-use:${use.service}->${use.api}`, 'unresolved use', `Uses ${use.service} → ${owner}#${use.api}`, { owner }));
192
+ }
193
+ }
194
+ /** `queue:Api.method` for a method-level hop; one `contract-queue:Api` per contract otherwise. */
195
+ queueIds(edge) {
196
+ return edge.queue === undefined ? edge.via.map((api) => `contract-queue:${api}`) : [`queue:${edge.queue}`];
197
+ }
198
+ services() {
199
+ const outgoing = new Map();
200
+ for (const fact of this.facts.values()) {
201
+ const from = fact.id.slice(fact.id.indexOf(':') + 1).split('->')[0];
202
+ if (this.graph.services[from] === undefined || fact.id.startsWith('deliver:'))
203
+ continue;
204
+ if (!outgoing.has(from))
205
+ outgoing.set(from, []);
206
+ outgoing.get(from).push(fact.id.slice(fact.id.indexOf('->') + 2) + (fact.properties['via'] === undefined ? '' : ` [${fact.properties['via']}]`));
207
+ }
208
+ for (const [name, service] of Object.entries(this.graph.services)) {
209
+ this.add(new RuntimeFact(`service:${name}`, RuntimeFactKind.NODE, 'service', `Service ${name}`, {
210
+ level: `L${service.level}`,
211
+ role: this.value(service.role),
212
+ serviceName: this.value(service.serviceName),
213
+ callsService: this.value(service.callsService === undefined ? undefined : this.canonical(service.callsService)),
214
+ implements: this.set(service.implements),
215
+ implementsVia: this.value(service.implementsVia === undefined ? undefined : this.canonical(service.implementsVia)),
216
+ products: this.set(service.products ?? []),
217
+ drawOnGraph: String(service.drawOnGraph !== false),
218
+ outgoing: this.set(outgoing.get(name) ?? []),
219
+ }));
220
+ }
221
+ }
222
+ queues() {
223
+ const flow = (queue) => ({
224
+ level: `L${queue.level}`,
225
+ selfQueue: String(queue.selfQueue),
226
+ producedBy: this.set(queue.producedBy),
227
+ consumedBy: this.set(queue.consumedBy),
228
+ });
229
+ for (const [key, queue] of Object.entries(this.graph.queues)) {
230
+ this.add(this.node(`queue:${key}`, 'queue', `Queue ${key}`, { ...flow(queue), queueName: queue.queueName }));
231
+ }
232
+ for (const [api, queue] of Object.entries(this.graph.contractQueues)) {
233
+ this.add(this.node(`contract-queue:${api}`, 'contract queue', `Contract queue ${api}`, flow(queue)));
234
+ }
235
+ }
236
+ externals() {
237
+ for (const [id, system] of Object.entries(this.graph.externalSystems ?? {})) {
238
+ this.add(this.node(`external-system:${id}`, 'external system', `External ${system.label}`, {
239
+ level: `L${system.level}`,
240
+ kind: system.kind,
241
+ label: system.label,
242
+ usedBy: this.set(system.usedBy),
243
+ apis: this.set(system.apis),
244
+ }));
245
+ }
246
+ const libraries = new Map();
247
+ for (const use of this.graph.unresolvedUses) {
248
+ if (this.graph.apis[use.api]?.externalSystem !== undefined)
249
+ continue;
250
+ const owner = this.owner(use.api);
251
+ if (!libraries.has(owner))
252
+ libraries.set(owner, new ExternalLibraryFacts(`L${use.level}`));
253
+ libraries.get(owner).apis.push(use.api);
254
+ libraries.get(owner).usedBy.push(use.service);
255
+ }
256
+ for (const owner of libraries.keys()) {
257
+ const facts = libraries.get(owner);
258
+ this.add(this.node(`external-library:${owner}`, 'external library', `External ${owner}`, {
259
+ level: facts.level,
260
+ apis: this.set(facts.apis),
261
+ usedBy: this.set(facts.usedBy),
262
+ }));
263
+ }
264
+ }
265
+ remotes() {
266
+ for (const [name, remote] of Object.entries(this.graph.remoteServices ?? {})) {
267
+ this.add(this.node(`remote-service:${name}`, 'remote service', `Remote service ${name}`, {
268
+ level: `L${remote.level}`,
269
+ label: this.value(remote.label),
270
+ implements: this.set(remote.implements),
271
+ }));
272
+ }
273
+ }
274
+ triggers() {
275
+ for (const trigger of this.graph.triggers) {
276
+ const caller = trigger.caller === undefined ? undefined : `${trigger.caller.kind} ${trigger.caller.label}`;
277
+ this.add(this.node(`trigger:${trigger.kind}:${trigger.api}.${trigger.method}@${trigger.service}`, `${trigger.kind} trigger`, `${trigger.kind === 'cron' ? 'Cron' : 'External'} ${trigger.api}.${trigger.method} → ${trigger.service}`, {
278
+ level: `L${trigger.level}`,
279
+ queueName: this.value(trigger.queueName),
280
+ caller: this.value(caller),
281
+ }));
282
+ }
283
+ }
284
+ contracts() {
285
+ for (const [name, api] of Object.entries(this.graph.apis)) {
286
+ this.add(new RuntimeFact(`api:${name}`, RuntimeFactKind.CONTRACT, 'contract', `Contract ${name}`, {
287
+ implementedBy: this.set(api.implementedBy),
288
+ usedBy: this.set(api.usedBy),
289
+ type: this.value(api.type),
290
+ owner: this.value(api.owner),
291
+ externalSystem: this.value(api.externalSystem === undefined ? undefined : `${api.externalSystem.kind} ${api.externalSystem.label ?? ''}`.trim()),
292
+ }));
293
+ }
294
+ }
295
+ owner(api) {
296
+ return this.graph.apis[api]?.owner ?? api;
297
+ }
298
+ node(id, category, label, properties) {
299
+ return new RuntimeFact(id, RuntimeFactKind.NODE, category, label, properties);
300
+ }
301
+ relation(id, category, label, properties) {
302
+ return new RuntimeFact(id, RuntimeFactKind.RELATIONSHIP, category, label, properties);
303
+ }
304
+ /** Set semantics: de-duplicated and sorted, so order alone is never a change. */
305
+ set(values) {
306
+ const unique = [...new Set(values)].sort();
307
+ return unique.length === 0 ? '(none)' : unique.join(', ');
308
+ }
309
+ split(value) {
310
+ return value === undefined || value === '(none)' ? [] : value.split(', ');
311
+ }
312
+ value(value) {
313
+ return value ?? '(absent)';
314
+ }
315
+ /** JSON with sorted keys, for the two map-or-string fields. */
316
+ canonical(value) {
317
+ if (typeof value === 'string')
318
+ return value;
319
+ return JSON.stringify(Object.keys(value).sort().map((key) => [key, value[key]]));
320
+ }
321
+ }
322
+ exports.RuntimeFactSet = RuntimeFactSet;
323
+ /** Compares two saved runtime graphs, Current → Goal. Pure; deterministic for equivalent inputs. */
324
+ class RuntimeGraphDiff {
325
+ compare(current, goal) {
326
+ const before = new RuntimeFactSet(current).facts;
327
+ const after = new RuntimeFactSet(goal).facts;
328
+ const ids = [...new Set([...before.keys(), ...after.keys()])].sort();
329
+ const changes = ids.map((id) => this.change(id, before.get(id), after.get(id)));
330
+ return new RuntimeGraphDiffResult(changes, current.maxLevel, goal.maxLevel);
331
+ }
332
+ change(id, before, after) {
333
+ const fact = (after ?? before);
334
+ if (before === undefined)
335
+ return new RuntimeFactChange(id, fact.kind, fact.category, fact.label, RuntimeDiffStatus.ADDED, null, after.properties, []);
336
+ if (after === undefined)
337
+ return new RuntimeFactChange(id, fact.kind, fact.category, fact.label, RuntimeDiffStatus.REMOVED, before.properties, null, []);
338
+ const fields = [...new Set([...Object.keys(before.properties), ...Object.keys(after.properties)])]
339
+ .sort()
340
+ .filter((field) => before.properties[field] !== after.properties[field]);
341
+ const status = fields.length === 0 ? RuntimeDiffStatus.UNCHANGED : RuntimeDiffStatus.CHANGED;
342
+ return new RuntimeFactChange(id, fact.kind, fact.category, fact.label, status, before.properties, after.properties, fields);
343
+ }
344
+ }
345
+ exports.RuntimeGraphDiff = RuntimeGraphDiff;
346
+ //# sourceMappingURL=runtime-graph-diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-graph-diff.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-graph-diff.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Goal vs Current (#1239): a SEMANTIC diff of two saved RuntimeGraph files.\n *\n * Both graphs are flattened into FACTS keyed by stable underlying identities — a service's project\n * key, a queue's `Api.method`, a contract queue's API, an external system's identity — never by a\n * shortened display name or a renderer-generated edge id. A fact is a NODE (service, queue, contract\n * queue, external system, remote service, external library, trigger), a RELATIONSHIP (rpc call,\n * enqueue, delivery, external use, remote call, unresolved use) or a CONTRACT (an `apis` entry).\n *\n * Comparison policy:\n * - Values are canonical strings. Set-like arrays are de-duplicated and sorted, so object key order\n * and array order alone never produce a change.\n * - Queue facts are compared per method / per contract BEFORE presentation grouping, and every\n * pubsub edge is split into its enqueue and delivery relationships, so a merged queue box cannot\n * hide a difference between its members.\n * - Bookkeeping is ignored: `levelBecause` (explanatory text the level machinery regenerates),\n * `wiringUses` / `wiringImplements` (reviewed provenance), and `dependsOn` (the BUILD graph, which\n * this runtime comparison deliberately does not cover). `maxLevel` is reported in the summary.\n * - A service's outgoing relationships are one of its properties, so a service whose calls changed\n * is itself CHANGED and its details show the before/after call list.\n */\n\nimport type { RuntimeEdge, RuntimeGraph, RuntimeQueueFlow } from './runtime-graph-model';\n\nexport enum RuntimeDiffStatus {\n ADDED = 'added',\n REMOVED = 'removed',\n CHANGED = 'changed',\n UNCHANGED = 'unchanged',\n}\n\nexport enum RuntimeFactKind {\n NODE = 'node',\n RELATIONSHIP = 'relationship',\n CONTRACT = 'contract',\n}\n\n/** One identity's canonical properties in one graph. */\nexport class RuntimeFact {\n constructor(\n public readonly id: string,\n public readonly kind: RuntimeFactKind,\n public readonly category: string,\n public readonly label: string,\n public readonly properties: Record<string, string>,\n ) {}\n}\n\n/** One identity compared across Current → Goal. `before` is null when added, `after` when removed. */\nexport class RuntimeFactChange {\n constructor(\n public readonly id: string,\n public readonly kind: RuntimeFactKind,\n public readonly category: string,\n public readonly label: string,\n public readonly status: RuntimeDiffStatus,\n public readonly before: Record<string, string> | null,\n public readonly after: Record<string, string> | null,\n public readonly changedFields: string[],\n ) {}\n\n /** Human lines for a details panel: the status, then `field: before → after` per changed field. */\n lines(): string[] {\n const head = `${this.label} — ${this.status}`;\n if (this.status === RuntimeDiffStatus.ADDED) return [head, ...this.listing(this.after!)];\n if (this.status === RuntimeDiffStatus.REMOVED) return [head, ...this.listing(this.before!)];\n if (this.status === RuntimeDiffStatus.UNCHANGED) return [head];\n return [head, ...this.changedFields.map((field: string) => ` ${field}: ${this.before![field] ?? '(absent)'} → ${this.after![field] ?? '(absent)'}`)];\n }\n\n private listing(properties: Record<string, string>): string[] {\n return Object.keys(properties).map((field: string) => ` ${field}: ${properties[field]}`);\n }\n}\n\nexport class RuntimeDiffCounts {\n constructor(\n public readonly added: number,\n public readonly removed: number,\n public readonly changed: number,\n public readonly unchanged: number,\n ) {}\n}\n\n/** Every compared identity, sorted by id, plus graph-level bookkeeping. */\nexport class RuntimeGraphDiffResult {\n private readonly byId = new Map<string, RuntimeFactChange>();\n\n constructor(\n public readonly changes: RuntimeFactChange[],\n public readonly maxLevelBefore: number,\n public readonly maxLevelAfter: number,\n ) {\n for (const change of changes) this.byId.set(change.id, change);\n }\n\n get(id: string): RuntimeFactChange | undefined {\n return this.byId.get(id);\n }\n\n counts(): RuntimeDiffCounts {\n const count = (status: RuntimeDiffStatus): number => this.changes.filter((change: RuntimeFactChange) => change.status === status).length;\n return new RuntimeDiffCounts(\n count(RuntimeDiffStatus.ADDED),\n count(RuntimeDiffStatus.REMOVED),\n count(RuntimeDiffStatus.CHANGED),\n count(RuntimeDiffStatus.UNCHANGED),\n );\n }\n\n identical(): boolean {\n return this.changes.every((change: RuntimeFactChange) => change.status === RuntimeDiffStatus.UNCHANGED);\n }\n\n /** The changed identities only (added, removed, changed), in id order. */\n differences(): RuntimeFactChange[] {\n return this.changes.filter((change: RuntimeFactChange) => change.status !== RuntimeDiffStatus.UNCHANGED);\n }\n}\n\n/** One vendor library's unresolved uses, gathered before they become a fact. */\nclass ExternalLibraryFacts {\n readonly apis: string[] = [];\n readonly usedBy: string[] = [];\n\n constructor(public readonly level: string) {}\n}\n\n/** Flattens one RuntimeGraph into its facts. */\nexport class RuntimeFactSet {\n readonly facts = new Map<string, RuntimeFact>();\n\n constructor(private readonly graph: RuntimeGraph) {\n this.relationships();\n this.services();\n this.queues();\n this.externals();\n this.remotes();\n this.triggers();\n this.contracts();\n }\n\n private add(fact: RuntimeFact): void {\n const existing = this.facts.get(fact.id);\n if (existing === undefined) {\n this.facts.set(fact.id, fact);\n return;\n }\n // Duplicate relationship facts (two rpc edges between one pair) fold into one, union of values.\n const merged: Record<string, string> = { ...existing.properties };\n for (const [key, value] of Object.entries(fact.properties)) merged[key] = this.set([...this.split(merged[key]), ...this.split(value)]);\n this.facts.set(fact.id, new RuntimeFact(fact.id, fact.kind, fact.category, fact.label, merged));\n }\n\n private relationships(): void {\n for (const edge of this.graph.runtimeEdges) {\n if (edge.type !== 'pubsub') {\n const props: Record<string, string> = { via: this.set(edge.via) };\n if (edge.cutLegacyCycle === true) props['cutLegacyCycle'] = 'true';\n this.add(this.relation(`rpc:${edge.from}->${edge.to}`, 'rpc call', `RPC ${edge.from} → ${edge.to}`, props));\n continue;\n }\n for (const queue of this.queueIds(edge)) {\n this.add(this.relation(`enqueue:${edge.from}->${queue}`, 'enqueue', `Enqueue ${edge.from} → ${queue}`, {}));\n this.add(this.relation(`deliver:${queue}->${edge.to}`, 'delivery', `Deliver ${queue} → ${edge.to}`, {}));\n }\n }\n for (const [id, system] of Object.entries(this.graph.externalSystems ?? {})) {\n for (const user of system.usedBy)\n this.add(this.relation(`external-use:${user}->external-system:${id}`, 'external use', `Uses ${user} → ${system.label}`, {}));\n }\n for (const [name, remote] of Object.entries(this.graph.remoteServices ?? {})) {\n for (const call of remote.calledBy)\n this.add(this.relation(`remote-call:${call.from}->remote-service:${name}#${call.type}`, 'remote call', `Remote ${call.type} ${call.from} → ${name}`, { via: this.set(call.via) }));\n }\n for (const use of this.graph.unresolvedUses) {\n const owner = this.owner(use.api);\n this.add(this.relation(`unresolved-use:${use.service}->${use.api}`, 'unresolved use', `Uses ${use.service} → ${owner}#${use.api}`, { owner }));\n }\n }\n\n /** `queue:Api.method` for a method-level hop; one `contract-queue:Api` per contract otherwise. */\n queueIds(edge: RuntimeEdge): string[] {\n return edge.queue === undefined ? edge.via.map((api: string) => `contract-queue:${api}`) : [`queue:${edge.queue}`];\n }\n\n private services(): void {\n const outgoing = new Map<string, string[]>();\n for (const fact of this.facts.values()) {\n const from = fact.id.slice(fact.id.indexOf(':') + 1).split('->')[0];\n if (this.graph.services[from] === undefined || fact.id.startsWith('deliver:')) continue;\n if (!outgoing.has(from)) outgoing.set(from, []);\n outgoing.get(from)!.push(fact.id.slice(fact.id.indexOf('->') + 2) + (fact.properties['via'] === undefined ? '' : ` [${fact.properties['via']}]`));\n }\n for (const [name, service] of Object.entries(this.graph.services)) {\n this.add(new RuntimeFact(`service:${name}`, RuntimeFactKind.NODE, 'service', `Service ${name}`, {\n level: `L${service.level}`,\n role: this.value(service.role),\n serviceName: this.value(service.serviceName),\n callsService: this.value(service.callsService === undefined ? undefined : this.canonical(service.callsService)),\n implements: this.set(service.implements),\n implementsVia: this.value(service.implementsVia === undefined ? undefined : this.canonical(service.implementsVia)),\n products: this.set(service.products ?? []),\n drawOnGraph: String(service.drawOnGraph !== false),\n outgoing: this.set(outgoing.get(name) ?? []),\n }));\n }\n }\n\n private queues(): void {\n const flow = (queue: RuntimeQueueFlow): Record<string, string> => ({\n level: `L${queue.level}`,\n selfQueue: String(queue.selfQueue),\n producedBy: this.set(queue.producedBy),\n consumedBy: this.set(queue.consumedBy),\n });\n for (const [key, queue] of Object.entries(this.graph.queues)) {\n this.add(this.node(`queue:${key}`, 'queue', `Queue ${key}`, { ...flow(queue), queueName: queue.queueName }));\n }\n for (const [api, queue] of Object.entries(this.graph.contractQueues)) {\n this.add(this.node(`contract-queue:${api}`, 'contract queue', `Contract queue ${api}`, flow(queue)));\n }\n }\n\n private externals(): void {\n for (const [id, system] of Object.entries(this.graph.externalSystems ?? {})) {\n this.add(this.node(`external-system:${id}`, 'external system', `External ${system.label}`, {\n level: `L${system.level}`,\n kind: system.kind,\n label: system.label,\n usedBy: this.set(system.usedBy),\n apis: this.set(system.apis),\n }));\n }\n const libraries = new Map<string, ExternalLibraryFacts>();\n for (const use of this.graph.unresolvedUses) {\n if (this.graph.apis[use.api]?.externalSystem !== undefined) continue;\n const owner = this.owner(use.api);\n if (!libraries.has(owner)) libraries.set(owner, new ExternalLibraryFacts(`L${use.level}`));\n libraries.get(owner)!.apis.push(use.api);\n libraries.get(owner)!.usedBy.push(use.service);\n }\n for (const owner of libraries.keys()) {\n const facts = libraries.get(owner)!;\n this.add(this.node(`external-library:${owner}`, 'external library', `External ${owner}`, {\n level: facts.level,\n apis: this.set(facts.apis),\n usedBy: this.set(facts.usedBy),\n }));\n }\n }\n\n private remotes(): void {\n for (const [name, remote] of Object.entries(this.graph.remoteServices ?? {})) {\n this.add(this.node(`remote-service:${name}`, 'remote service', `Remote service ${name}`, {\n level: `L${remote.level}`,\n label: this.value(remote.label),\n implements: this.set(remote.implements),\n }));\n }\n }\n\n private triggers(): void {\n for (const trigger of this.graph.triggers) {\n const caller = trigger.caller === undefined ? undefined : `${trigger.caller.kind} ${trigger.caller.label}`;\n this.add(this.node(`trigger:${trigger.kind}:${trigger.api}.${trigger.method}@${trigger.service}`, `${trigger.kind} trigger`,\n `${trigger.kind === 'cron' ? 'Cron' : 'External'} ${trigger.api}.${trigger.method} → ${trigger.service}`, {\n level: `L${trigger.level}`,\n queueName: this.value(trigger.queueName),\n caller: this.value(caller),\n }));\n }\n }\n\n private contracts(): void {\n for (const [name, api] of Object.entries(this.graph.apis)) {\n this.add(new RuntimeFact(`api:${name}`, RuntimeFactKind.CONTRACT, 'contract', `Contract ${name}`, {\n implementedBy: this.set(api.implementedBy),\n usedBy: this.set(api.usedBy),\n type: this.value(api.type),\n owner: this.value(api.owner),\n externalSystem: this.value(api.externalSystem === undefined ? undefined : `${api.externalSystem.kind} ${api.externalSystem.label ?? ''}`.trim()),\n }));\n }\n }\n\n private owner(api: string): string {\n return this.graph.apis[api]?.owner ?? api;\n }\n\n private node(id: string, category: string, label: string, properties: Record<string, string>): RuntimeFact {\n return new RuntimeFact(id, RuntimeFactKind.NODE, category, label, properties);\n }\n\n private relation(id: string, category: string, label: string, properties: Record<string, string>): RuntimeFact {\n return new RuntimeFact(id, RuntimeFactKind.RELATIONSHIP, category, label, properties);\n }\n\n /** Set semantics: de-duplicated and sorted, so order alone is never a change. */\n private set(values: string[]): string {\n const unique = [...new Set(values)].sort();\n return unique.length === 0 ? '(none)' : unique.join(', ');\n }\n\n private split(value: string | undefined): string[] {\n return value === undefined || value === '(none)' ? [] : value.split(', ');\n }\n\n private value(value: string | undefined): string {\n return value ?? '(absent)';\n }\n\n /** JSON with sorted keys, for the two map-or-string fields. */\n private canonical(value: string | Record<string, string>): string {\n if (typeof value === 'string') return value;\n return JSON.stringify(Object.keys(value).sort().map((key: string) => [key, value[key]]));\n }\n}\n\n/** Compares two saved runtime graphs, Current → Goal. Pure; deterministic for equivalent inputs. */\nexport class RuntimeGraphDiff {\n compare(current: RuntimeGraph, goal: RuntimeGraph): RuntimeGraphDiffResult {\n const before = new RuntimeFactSet(current).facts;\n const after = new RuntimeFactSet(goal).facts;\n const ids = [...new Set([...before.keys(), ...after.keys()])].sort();\n const changes = ids.map((id: string) => this.change(id, before.get(id), after.get(id)));\n return new RuntimeGraphDiffResult(changes, current.maxLevel, goal.maxLevel);\n }\n\n private change(id: string, before: RuntimeFact | undefined, after: RuntimeFact | undefined): RuntimeFactChange {\n const fact = (after ?? before)!;\n if (before === undefined)\n return new RuntimeFactChange(id, fact.kind, fact.category, fact.label, RuntimeDiffStatus.ADDED, null, after!.properties, []);\n if (after === undefined)\n return new RuntimeFactChange(id, fact.kind, fact.category, fact.label, RuntimeDiffStatus.REMOVED, before.properties, null, []);\n const fields = [...new Set([...Object.keys(before.properties), ...Object.keys(after.properties)])]\n .sort()\n .filter((field: string) => before.properties[field] !== after.properties[field]);\n const status = fields.length === 0 ? RuntimeDiffStatus.UNCHANGED : RuntimeDiffStatus.CHANGED;\n return new RuntimeFactChange(id, fact.kind, fact.category, fact.label, status, before.properties, after.properties, fields);\n }\n}\n"]}
@@ -14,6 +14,14 @@ export declare class RuntimeHtmlPage {
14
14
  private designLinks;
15
15
  private lockControl;
16
16
  private legend;
17
+ /**
18
+ * The products some drawn box carries, colored from the ONE palette the boxes were painted with —
19
+ * built over every declared product (#1244), so a product keeps the dependency viewer's color even
20
+ * though this page lists only the products it shows.
21
+ */
22
+ private shownProducts;
23
+ /** Goal vs Current (#1239): the direction, the counts (or why it is unavailable), the key and every difference. */
24
+ private goalLegend;
17
25
  private edgeKey;
18
26
  private responsibilities;
19
27
  }
@@ -8,6 +8,10 @@ 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
+ const runtime_goal_details_1 = require("./runtime-goal-details");
13
+ const runtime_goal_styles_1 = require("./runtime-goal-styles");
14
+ const runtime_graph_diff_1 = require("./runtime-graph-diff");
11
15
  const graph_navigation_1 = require("./graph-navigation");
12
16
  const saved_snapshot_1 = require("./saved-snapshot");
13
17
  const graph_visualizer_2 = require("./graph-visualizer");
@@ -15,7 +19,6 @@ const graph_node_menu_1 = require("./graph-node-menu");
15
19
  const graph_page_shell_1 = require("./graph-page-shell");
16
20
  const graph_page_styles_1 = require("./graph-page-styles");
17
21
  const graph_legend_1 = require("./graph-legend");
18
- const graph_products_1 = require("./graph-products");
19
22
  const graph_color_modes_1 = require("./graph-color-modes");
20
23
  const graph_impact_1 = require("./graph-impact");
21
24
  const graph_responsibilities_1 = require("./graph-responsibilities");
@@ -41,8 +44,8 @@ class RuntimeHtmlPage {
41
44
  const roles = [
42
45
  ...new Set(model.nodes.flatMap((node) => (node.tags === null ? [] : [node.tags.role]))),
43
46
  ].sort();
44
- const products = [...new Set(model.nodes.flatMap((node) => node.products))].sort();
45
- 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>');
47
+ 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>');
46
49
  const shared = this.sharedJs()
47
50
  .split(graph_visualizer_2.CLIENT_MODEL_PLACEHOLDER)
48
51
  .join(this.assets.json(model))
@@ -53,13 +56,15 @@ class RuntimeHtmlPage {
53
56
  <script src="https://cdn.jsdelivr.net/npm/@viz-js/viz@3.28.0/dist/viz-global.js"></script>
54
57
  <script src="${graph_impact_1.IMPACT_SIDECAR_SRC}"></script>
55
58
  <style>${this.menu.styles()}${this.menu.dimStyles('#graph')}${this.assets.styles()}${this.navigation.styles()}${new graph_page_styles_1.GraphPageStyles().css()}
56
- .wp-graph-details { 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; }
57
- .wp-graph-details button { cursor:pointer; } .wp-api-detail { cursor:pointer; text-decoration:underline; }
59
+ .wp-graph-details, .wp-goal-details { 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; }
60
+ .wp-graph-details button, .wp-goal-details button { cursor:pointer; } .wp-api-detail { cursor:pointer; text-decoration:underline; }
58
61
  .wp-api-detail:focus-visible { outline:3px solid var(--wp-accent); } #wp-context-count { font-size:11px; color:var(--wp-muted); }
62
+ .wp-goal-edge { cursor:pointer; } .wp-goal-diff ul { margin:4px 0 0; padding-left:16px; max-height:220px; overflow:auto; }
59
63
  .wp-empty { position:absolute; top:70px; left:16px; padding:16px; background:var(--wp-surface); border-radius:10px; }
60
64
  </style></head><body data-wp-project-facts="${options.projects !== null}">${new graph_page_shell_1.GraphPageShell().body(parts)}
61
65
  <script>${this.menu.script()}</script><script>${this.filterJs()}</script><script>${this.navigation.script()}</script>
62
66
  <script>${new runtime_details_1.RuntimeDetails(graph, options.showExternalNodes, model).script()}</script>
67
+ <script>${new runtime_goal_details_1.RuntimeGoalDetails(model.comparison).script()}</script>
63
68
  <script>${this.clientJs()}</script><script>${shared}</script></body></html>`;
64
69
  }
65
70
  designLinks(graph, options) {
@@ -93,14 +98,42 @@ class RuntimeHtmlPage {
93
98
  ]
94
99
  .map((role) => tag('role', role, graph_color_modes_1.ROLE_STYLES.find((style) => style.role === role)?.fill ?? '#e4e6ec'))
95
100
  .join('');
96
- const products = new graph_products_1.ProductPalette(model.legend.products.map((count) => count.product)).colors
101
+ const products = this.shownProducts(model)
97
102
  .map((color) => tag('product', color.product, color.color))
98
103
  .join('');
99
104
  return (`<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>
100
105
  <section data-wp-legend-mode="architecture" class="wp-legend-list" hidden>${roles}<p>Auxiliary nodes use kind colors, never project roles.</p></section>` +
101
106
  `<section data-wp-legend-mode="impact" class="wp-legend-list" hidden>${new graph_legend_1.GraphLegend().impactRows()}</section><section data-wp-legend-mode="product" class="wp-legend-list" hidden>${products}<p>Stripes: several products; neutral: all products; white/dashed: no product. Context inherits adjacent service memberships.</p></section>` +
107
+ this.goalLegend(model) +
102
108
  '<p>Queues, clocks, datastores and vendors remain context in Impact; no source ownership or CI status is implied.</p>');
103
109
  }
110
+ /**
111
+ * The products some drawn box carries, colored from the ONE palette the boxes were painted with —
112
+ * built over every declared product (#1244), so a product keeps the dependency viewer's color even
113
+ * though this page lists only the products it shows.
114
+ */
115
+ shownProducts(model) {
116
+ const shown = new Set(model.nodes.flatMap((node) => node.products));
117
+ return model.legend.palette.colors.filter((color) => shown.has(color.product));
118
+ }
119
+ /** Goal vs Current (#1239): the direction, the counts (or why it is unavailable), the key and every difference. */
120
+ goalLegend(model) {
121
+ const comparison = model.comparison;
122
+ if (comparison === null || !comparison.available)
123
+ return `<section data-wp-legend-mode="goal" class="wp-legend-list" hidden><p>Goal vs Current unavailable: ${(0, dot_syntax_1.htmlLabelText)(comparison?.reason ?? 'no comparison data on this page')}</p></section>`;
124
+ const rows = [runtime_graph_diff_1.RuntimeDiffStatus.ADDED, runtime_graph_diff_1.RuntimeDiffStatus.REMOVED, runtime_graph_diff_1.RuntimeDiffStatus.CHANGED, runtime_graph_diff_1.RuntimeDiffStatus.UNCHANGED]
125
+ .map((status) => {
126
+ const style = runtime_goal_styles_1.DIFF_STYLES[status];
127
+ const dash = status === runtime_graph_diff_1.RuntimeDiffStatus.REMOVED ? ' stroke-dasharray="3 2"' : '';
128
+ 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>`;
129
+ })
130
+ .join('');
131
+ const differences = comparison.differences.map((line) => `<li>${(0, dot_syntax_1.htmlLabelText)(line)}</li>`).join('');
132
+ return (`<section data-wp-legend-mode="goal" class="wp-legend-list wp-goal-diff" hidden><p><strong>${(0, dot_syntax_1.htmlLabelText)(comparison.direction)}</strong> · ${(0, dot_syntax_1.htmlLabelText)(comparison.summary)}</p>${rows}` +
133
+ '<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>' +
134
+ (differences === '' ? '' : `<details><summary>Differences (${comparison.differences.length})</summary><ul>${differences}</ul></details>`) +
135
+ '</section>');
136
+ }
104
137
  edgeKey() {
105
138
  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>';
106
139
  }
@@ -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 } from './runtime-viz-options';\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 { ProductPalette } 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 = [...new Set(model.nodes.flatMap((node) => node.products))].sort();\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 );\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 { 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 { 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-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>${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 products = new ProductPalette(\n model.legend.products.map((count) => count.product),\n ).colors\n .map((color) => tag('product', color.product, color.color))\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>${new GraphLegend().impactRows()}</section><section data-wp-legend-mode=\"product\" class=\"wp-legend-list\" hidden>${products}<p>Stripes: several products; neutral: all products; white/dashed: no product. Context inherits adjacent service memberships.</p></section>` +\n '<p>Queues, clocks, datastores and vendors remain context in Impact; no source ownership or CI status is implied.</p>'\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, 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 { 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 { 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 products = this.shownProducts(model)\n .map((color) => tag('product', color.product, color.color))\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>${new GraphLegend().impactRows()}</section><section data-wp-legend-mode=\"product\" class=\"wp-legend-list\" hidden>${products}<p>Stripes: several products; neutral: all products; white/dashed: no product. Context inherits adjacent service memberships.</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"]}
@@ -2,5 +2,7 @@ import { GraphRenderModel } from './graph-render-model';
2
2
  /** Saved levels own layout; real feedback arrows remain visible without moving ranks. */
3
3
  export declare class RuntimeLayout {
4
4
  render(model: GraphRenderModel): string;
5
+ /** The DOT for the model's current statements, without re-deciding constraints (restyled overlays). */
6
+ assemble(model: GraphRenderModel): string;
5
7
  private unconstrained;
6
8
  }
@@ -17,6 +17,10 @@ class RuntimeLayout {
17
17
  continue;
18
18
  model.edges[index] = new graph_render_model_1.RenderEdge(edge.from, edge.to, this.unconstrained(edge.dot), edge.id, edge.productDot === null ? null : this.unconstrained(edge.productDot));
19
19
  }
20
+ return this.assemble(model);
21
+ }
22
+ /** The DOT for the model's current statements, without re-deciding constraints (restyled overlays). */
23
+ assemble(model) {
20
24
  return (model.header +
21
25
  model.nodes.map((node) => node.dot).join('') +
22
26
  new graph_level_bands_1.LevelBandLayout().dot(model.bands) +