@webpieces/nx-webpieces-rules 0.4.970 → 0.4.971

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/nx-webpieces-rules",
3
- "version": "0.4.970",
3
+ "version": "0.4.971",
4
4
  "description": "Nx-specific webpieces validation rules, graph tooling, executors, and inference plugin.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -20,12 +20,12 @@
20
20
  "README.md"
21
21
  ],
22
22
  "dependencies": {
23
- "@webpieces/api-doc-model": "0.4.970",
24
- "@webpieces/core-util": "0.4.970",
25
- "@webpieces/eslint-rules": "0.4.970",
26
- "@webpieces/rules-config": "0.4.970",
23
+ "@webpieces/api-doc-model": "0.4.971",
24
+ "@webpieces/core-util": "0.4.971",
25
+ "@webpieces/eslint-rules": "0.4.971",
26
+ "@webpieces/rules-config": "0.4.971",
27
27
  "madge": "8.0.0",
28
- "@webpieces/rules-sdk": "0.4.970"
28
+ "@webpieces/rules-sdk": "0.4.971"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "@nx/devkit": ">=18.0.0"
@@ -41,7 +41,7 @@
41
41
  "access": "public"
42
42
  },
43
43
  "devDependencies": {
44
- "@webpieces/tooling-testkit": "0.4.970"
44
+ "@webpieces/tooling-testkit": "0.4.971"
45
45
  },
46
46
  "types": "./src/index.d.ts",
47
47
  "bin": {
@@ -0,0 +1,18 @@
1
+ import type { RuntimeEdge } from './runtime-graph-model';
2
+ /**
3
+ * The ONE grouping step for runtime edges (#1370): at most one edge per (transport, from, to, queue),
4
+ * carrying every contract that flows over it.
5
+ *
6
+ * The generator already emits edges in this shape (it keys its derivation the same way), so for a
7
+ * generated graph this is the identity. A hand- or AI-authored goal file is not held to it: it can
8
+ * list `portal-gateway -> delivery-partner-outbound` three times, once per contract, and every view
9
+ * then drew three parallel arrows where every other pair draws ONE `Uses (N) ▾` arrow — and the
10
+ * details popup, keyed by the pair, kept only the last contract. Merging here, where a graph that did
11
+ * not come from the generator enters the viewer, makes every downstream view (the goal page, the
12
+ * Goal vs Current overlay, the details) see the generator's shape without a special case of its own.
13
+ */
14
+ export declare class RuntimeEdgeMerge {
15
+ /** The merged edges, in first-appearance order; `via` is the sorted union. Inputs are not mutated. */
16
+ merge(edges: RuntimeEdge[]): RuntimeEdge[];
17
+ private key;
18
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RuntimeEdgeMerge = void 0;
4
+ /**
5
+ * The ONE grouping step for runtime edges (#1370): at most one edge per (transport, from, to, queue),
6
+ * carrying every contract that flows over it.
7
+ *
8
+ * The generator already emits edges in this shape (it keys its derivation the same way), so for a
9
+ * generated graph this is the identity. A hand- or AI-authored goal file is not held to it: it can
10
+ * list `portal-gateway -> delivery-partner-outbound` three times, once per contract, and every view
11
+ * then drew three parallel arrows where every other pair draws ONE `Uses (N) ▾` arrow — and the
12
+ * details popup, keyed by the pair, kept only the last contract. Merging here, where a graph that did
13
+ * not come from the generator enters the viewer, makes every downstream view (the goal page, the
14
+ * Goal vs Current overlay, the details) see the generator's shape without a special case of its own.
15
+ */
16
+ class RuntimeEdgeMerge {
17
+ /** The merged edges, in first-appearance order; `via` is the sorted union. Inputs are not mutated. */
18
+ merge(edges) {
19
+ const byKey = new Map();
20
+ for (const edge of edges) {
21
+ const key = this.key(edge);
22
+ const kept = byKey.get(key);
23
+ if (kept === undefined) {
24
+ byKey.set(key, { ...edge, via: [...edge.via] });
25
+ continue;
26
+ }
27
+ kept.via = [...new Set([...kept.via, ...edge.via])].sort();
28
+ if (edge.cutLegacyCycle === true)
29
+ kept.cutLegacyCycle = true;
30
+ }
31
+ return [...byKey.values()];
32
+ }
33
+ key(edge) {
34
+ return `${edge.type ?? 'rpc'} ${edge.from} ${edge.to} ${edge.queue ?? ''}`;
35
+ }
36
+ }
37
+ exports.RuntimeEdgeMerge = RuntimeEdgeMerge;
38
+ //# sourceMappingURL=runtime-edge-merge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-edge-merge.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-edge-merge.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;GAWG;AACH,MAAa,gBAAgB;IACzB,sGAAsG;IACtG,KAAK,CAAC,KAAoB;QACtB,MAAM,KAAK,GAAG,IAAI,GAAG,EAAuB,CAAC;QAC7C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC3B,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC5B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACrB,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAChD,SAAS;YACb,CAAC;YACD,IAAI,CAAC,GAAG,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC3D,IAAI,IAAI,CAAC,cAAc,KAAK,IAAI;gBAAE,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QACjE,CAAC;QACD,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/B,CAAC;IAEO,GAAG,CAAC,IAAiB;QACzB,OAAO,GAAG,IAAI,CAAC,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;IAC/E,CAAC;CACJ;AApBD,4CAoBC","sourcesContent":["import type { RuntimeEdge } from './runtime-graph-model';\n\n/**\n * The ONE grouping step for runtime edges (#1370): at most one edge per (transport, from, to, queue),\n * carrying every contract that flows over it.\n *\n * The generator already emits edges in this shape (it keys its derivation the same way), so for a\n * generated graph this is the identity. A hand- or AI-authored goal file is not held to it: it can\n * list `portal-gateway -> delivery-partner-outbound` three times, once per contract, and every view\n * then drew three parallel arrows where every other pair draws ONE `Uses (N) ▾` arrow — and the\n * details popup, keyed by the pair, kept only the last contract. Merging here, where a graph that did\n * not come from the generator enters the viewer, makes every downstream view (the goal page, the\n * Goal vs Current overlay, the details) see the generator's shape without a special case of its own.\n */\nexport class RuntimeEdgeMerge {\n /** The merged edges, in first-appearance order; `via` is the sorted union. Inputs are not mutated. */\n merge(edges: RuntimeEdge[]): RuntimeEdge[] {\n const byKey = new Map<string, RuntimeEdge>();\n for (const edge of edges) {\n const key = this.key(edge);\n const kept = byKey.get(key);\n if (kept === undefined) {\n byKey.set(key, { ...edge, via: [...edge.via] });\n continue;\n }\n kept.via = [...new Set([...kept.via, ...edge.via])].sort();\n if (edge.cutLegacyCycle === true) kept.cutLegacyCycle = true;\n }\n return [...byKey.values()];\n }\n\n private key(edge: RuntimeEdge): string {\n return `${edge.type ?? 'rpc'} ${edge.from} ${edge.to} ${edge.queue ?? ''}`;\n }\n}\n"]}
@@ -19,6 +19,7 @@ const path = tslib_1.__importStar(require("path"));
19
19
  const rules_config_1 = require("@webpieces/rules-config");
20
20
  const runtime_graph_io_1 = require("./runtime-graph-io");
21
21
  const runtime_node_levels_1 = require("./runtime-node-levels");
22
+ const runtime_edge_merge_1 = require("./runtime-edge-merge");
22
23
  const runtime_goal_validator_1 = require("./runtime-goal-validator");
23
24
  const toError_1 = require("../toError");
24
25
  exports.DEFAULT_RUNTIME_GOAL_PATH = 'architecture/runtime-dependencies-goal.json';
@@ -119,6 +120,8 @@ class RuntimeGoalFile {
119
120
  const problems = this.validator.structural(parsed);
120
121
  if (problems.length > 0)
121
122
  throw this.failure(`${this.goalPath} is not a usable runtime goal:\n ${problems.join('\n ')}`, [this.editOption(true)]);
123
+ // A goal is hand-authored, so it may list one pair once per contract; every view draws ONE arrow per pair.
124
+ parsed.runtimeEdges = new runtime_edge_merge_1.RuntimeEdgeMerge().merge(parsed.runtimeEdges);
122
125
  return parsed;
123
126
  }
124
127
  /** A deep copy with the generator's level machinery applied, or the cycle it refused. */
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-goal-file.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-goal-file.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The runtime architecture GOAL (#1239): `architecture/runtime-dependencies-goal.json`.\n *\n * A proposal, not a fact. `architecture/runtime-dependencies.json` is GENERATED from source and its\n * unchanged-validator rightly treats any hand edit as drift, so a proposal needs its own durable,\n * reviewable file in the same RuntimeGraph shape. Nothing in the generator reads, writes or deletes\n * this file: only a person (or an AI) edits it, and only the explicit `--prepare` step below rewrites\n * its derived fields.\n *\n * Errors here never tell anyone to regenerate from source — a goal has no source. They name the goal\n * path, the broken element, and the edit or the explicit preparation command that repairs it.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { RuleFailError, Option } from '@webpieces/rules-config';\nimport type { RuntimeGraph, RuntimeQueueFlow, RuntimeTrigger } from './runtime-graph-model';\nimport { DEFAULT_RUNTIME_GRAPH_PATH, saveRuntimeGraph } from './runtime-graph-io';\nimport { RuntimeNodeLevels } from './runtime-node-levels';\nimport { RuntimeGoalValidator } from './runtime-goal-validator';\nimport { toError } from '../toError';\n\nexport const DEFAULT_RUNTIME_GOAL_PATH = 'architecture/runtime-dependencies-goal.json';\nexport const RUNTIME_GOAL_RULE_NAME = 'runtime-architecture-goal';\nexport const RUNTIME_GOAL_PREPARE_COMMAND = 'pnpm nx run architecture:visualize-runtime-goal --prepare';\nexport const RUNTIME_GOAL_INIT_COMMAND = `cp ${DEFAULT_RUNTIME_GRAPH_PATH} ${DEFAULT_RUNTIME_GOAL_PATH}`;\n\n/** The derived fields `--prepare` rewrote, as `field: before → after` lines; empty when none moved. */\nexport class RuntimeGoalPreparation {\n constructor(\n public readonly graph: RuntimeGraph,\n public readonly changes: string[],\n /** Whether the goal file was rewritten (also true when only explanatory levelBecause text moved). */\n public readonly written: boolean,\n ) {}\n}\n\n/**\n * What viewing found about the goal's derived facts: a CYCLE (only an edit of runtimeEdges fixes it,\n * `--prepare` refuses it too) or field DIFFERENCES that `--prepare` would rewrite.\n */\nexport class RuntimeGoalDrift {\n constructor(\n public readonly cycle: string | null,\n public readonly differences: string[],\n ) {}\n}\n\n/** Loads, checks and (only on request) prepares the hand-authored runtime goal. */\nexport class RuntimeGoalFile {\n private readonly validator: RuntimeGoalValidator;\n\n constructor(public readonly goalPath: string = DEFAULT_RUNTIME_GOAL_PATH) {\n this.validator = new RuntimeGoalValidator(goalPath);\n }\n\n exists(workspaceRoot: string): boolean {\n return fs.existsSync(path.join(workspaceRoot, this.goalPath));\n }\n\n /** The goal as saved, after its shape, references and derived facts check out. Never writes anything. */\n load(workspaceRoot: string): RuntimeGraph {\n const parsed = this.structurallyValid(workspaceRoot);\n const derived = this.validator.derived(parsed);\n if (derived.length > 0)\n throw this.failure(`${this.goalPath} lacks derived facts the viewer needs:\\n ${derived.join('\\n ')}`, [\n new Option(`Compute them from the goal's relationships: ${RUNTIME_GOAL_PREPARE_COMMAND}`, true),\n this.editOption(false),\n ]);\n return parsed;\n }\n\n /**\n * Where the saved derived facts disagree with what the existing runtime level machinery\n * (RuntimeNodeLevels — the generator's own) computes from the goal's relationships. Reported, not\n * fixed: viewing is read-only, and `--prepare` is the explicit step that rewrites them.\n */\n drift(graph: RuntimeGraph): RuntimeGoalDrift {\n const recomputed = this.recompute(graph);\n if (typeof recomputed === 'string') return new RuntimeGoalDrift(recomputed, []);\n return new RuntimeGoalDrift(null, this.differences(graph, recomputed));\n }\n\n /**\n * Recompute levels, self-queue flags, contract queues and maxLevel, and WRITE the goal. Gated on\n * STRUCTURAL problems only: missing or stale derived facts are exactly what this step computes.\n */\n prepare(workspaceRoot: string): RuntimeGoalPreparation {\n const saved = this.structurallyValid(workspaceRoot);\n const recomputed = this.recompute(saved);\n if (typeof recomputed === 'string') throw this.failure(recomputed, [this.cycleOption()]);\n const changes = this.differences(saved, recomputed);\n const written = JSON.stringify(saved) !== JSON.stringify(recomputed);\n if (written) saveRuntimeGraph(recomputed, workspaceRoot, this.goalPath);\n return new RuntimeGoalPreparation(recomputed, changes, written);\n }\n\n /** The edit that clears a cycle verdict — `--prepare` cannot. */\n cycleOption(): Option {\n return new Option(`Break the cycle by editing \"runtimeEdges\" (or the queues it flows through) in ${this.goalPath}.`, true);\n }\n\n private structurallyValid(workspaceRoot: string): RuntimeGraph {\n if (!this.exists(workspaceRoot)) throw this.missing();\n let parsed: RuntimeGraph;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- hand-authored JSON decoding boundary adds the goal path and repair while preserving the cause\n try {\n parsed = JSON.parse(fs.readFileSync(path.join(workspaceRoot, this.goalPath), 'utf-8')) as RuntimeGraph;\n } catch (err: unknown) {\n const error = toError(err);\n throw this.failure(`${this.goalPath} is not valid JSON: ${error.message}`, [this.editOption(true)], error);\n }\n const problems = this.validator.structural(parsed);\n if (problems.length > 0)\n throw this.failure(`${this.goalPath} is not a usable runtime goal:\\n ${problems.join('\\n ')}`, [this.editOption(true)]);\n return parsed;\n }\n\n /** A deep copy with the generator's level machinery applied, or the cycle it refused. */\n private recompute(graph: RuntimeGraph): RuntimeGraph | string {\n const copy = JSON.parse(JSON.stringify(graph)) as RuntimeGraph;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- the shared level machinery throws its cycle verdict; the goal reports it against its own path\n try {\n new RuntimeNodeLevels().apply(copy);\n return copy;\n } catch (err: unknown) {\n const error = toError(err);\n if (!(error instanceof RuleFailError)) throw error;\n return `${this.goalPath} describes a runtime graph the level machinery cannot order: ${error.message}`;\n }\n }\n\n private differences(saved: RuntimeGraph, recomputed: RuntimeGraph): string[] {\n const lines: string[] = [];\n const level = (where: string, before: number | undefined, after: number): void => {\n if (before !== after) lines.push(`${where} level: L${before ?? '?'} → L${after}`);\n };\n for (const [name, service] of Object.entries(recomputed.services)) level(`services[\"${name}\"]`, saved.services[name].level, service.level);\n for (const [key, queue] of Object.entries(recomputed.queues)) {\n level(`queues[\"${key}\"]`, saved.queues[key].level, queue.level);\n if (saved.queues[key].selfQueue !== queue.selfQueue)\n lines.push(`queues[\"${key}\"] selfQueue: ${saved.queues[key].selfQueue} → ${queue.selfQueue}`);\n }\n lines.push(...this.contractDifferences(saved.contractQueues ?? {}, recomputed.contractQueues));\n recomputed.triggers.forEach((trigger: RuntimeTrigger, index: number) =>\n level(`triggers[${index}]`, saved.triggers[index].level, trigger.level),\n );\n if (saved.maxLevel !== recomputed.maxLevel) lines.push(`maxLevel: ${saved.maxLevel} → ${recomputed.maxLevel}`);\n return lines;\n }\n\n private contractDifferences(saved: Record<string, RuntimeQueueFlow>, recomputed: Record<string, RuntimeQueueFlow>): string[] {\n const lines: string[] = [];\n const fields = (flow: RuntimeQueueFlow | undefined): string =>\n flow === undefined\n ? '(absent)'\n : `L${flow.level} self=${flow.selfQueue} producers=[${flow.producedBy.join(', ')}] consumers=[${flow.consumedBy.join(', ')}]`;\n for (const api of [...new Set([...Object.keys(saved), ...Object.keys(recomputed)])].sort()) {\n const before = fields(saved[api]);\n const after = fields(recomputed[api]);\n if (before !== after) lines.push(`contractQueues[\"${api}\"]: ${before} → ${after}`);\n }\n return lines;\n }\n\n private missing(): RuleFailError {\n return this.failure(\n `No runtime goal at ${this.goalPath}. Viewing never creates or replaces the goal, and never renders the current graph in its place.`,\n [\n new Option(`Start a proposal from the saved current runtime graph: ${RUNTIME_GOAL_INIT_COMMAND} — then edit the copy and commit it.`, true),\n new Option(`Write ${this.goalPath} by hand in the RuntimeGraph shape of ${DEFAULT_RUNTIME_GRAPH_PATH}.`),\n ],\n );\n }\n\n private editOption(preferred: boolean): Option {\n return new Option(`Edit ${this.goalPath} by hand — it is a hand-authored proposal, so the fix is in the file, not in source.`, preferred);\n }\n\n private failure(message: string, options: Option[], cause?: Error): RuleFailError {\n return new RuleFailError(RUNTIME_GOAL_RULE_NAME, message, undefined, undefined, options, undefined, cause);\n }\n}\n\n/** What a goal page says it is: a hand-authored proposal, not a generated snapshot (#1239). */\nexport class RuntimeGoalProvenance {\n message(): string {\n return (\n `Goal / Proposed runtime architecture from ${DEFAULT_RUNTIME_GOAL_PATH}. Hand-authored; not generated from source, ` +\n 'and never written by architecture:generate. Viewing changes no file. ' +\n `After editing relationships, recompute derived levels explicitly: ${RUNTIME_GOAL_PREPARE_COMMAND}`\n );\n }\n\n html(): string {\n return (\n `<p class=\"hint\"><strong>Goal / Proposed runtime architecture.</strong> Hand-authored proposal read from ` +\n `<code>${DEFAULT_RUNTIME_GOAL_PATH}</code>; not generated from source and never written by ` +\n `<code>architecture:generate</code>. Viewing changes no file. After editing relationships, recompute derived ` +\n `levels explicitly: <code>${RUNTIME_GOAL_PREPARE_COMMAND}</code>. Compare with the saved current graph ` +\n `(<code>${DEFAULT_RUNTIME_GRAPH_PATH}</code>) in Color by → Goal vs Current.</p>`\n );\n }\n}\n"]}
1
+ {"version":3,"file":"runtime-goal-file.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-goal-file.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The runtime architecture GOAL (#1239): `architecture/runtime-dependencies-goal.json`.\n *\n * A proposal, not a fact. `architecture/runtime-dependencies.json` is GENERATED from source and its\n * unchanged-validator rightly treats any hand edit as drift, so a proposal needs its own durable,\n * reviewable file in the same RuntimeGraph shape. Nothing in the generator reads, writes or deletes\n * this file: only a person (or an AI) edits it, and only the explicit `--prepare` step below rewrites\n * its derived fields.\n *\n * Errors here never tell anyone to regenerate from source — a goal has no source. They name the goal\n * path, the broken element, and the edit or the explicit preparation command that repairs it.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { RuleFailError, Option } from '@webpieces/rules-config';\nimport type { RuntimeGraph, RuntimeQueueFlow, RuntimeTrigger } from './runtime-graph-model';\nimport { DEFAULT_RUNTIME_GRAPH_PATH, saveRuntimeGraph } from './runtime-graph-io';\nimport { RuntimeNodeLevels } from './runtime-node-levels';\nimport { RuntimeEdgeMerge } from './runtime-edge-merge';\nimport { RuntimeGoalValidator } from './runtime-goal-validator';\nimport { toError } from '../toError';\n\nexport const DEFAULT_RUNTIME_GOAL_PATH = 'architecture/runtime-dependencies-goal.json';\nexport const RUNTIME_GOAL_RULE_NAME = 'runtime-architecture-goal';\nexport const RUNTIME_GOAL_PREPARE_COMMAND = 'pnpm nx run architecture:visualize-runtime-goal --prepare';\nexport const RUNTIME_GOAL_INIT_COMMAND = `cp ${DEFAULT_RUNTIME_GRAPH_PATH} ${DEFAULT_RUNTIME_GOAL_PATH}`;\n\n/** The derived fields `--prepare` rewrote, as `field: before → after` lines; empty when none moved. */\nexport class RuntimeGoalPreparation {\n constructor(\n public readonly graph: RuntimeGraph,\n public readonly changes: string[],\n /** Whether the goal file was rewritten (also true when only explanatory levelBecause text moved). */\n public readonly written: boolean,\n ) {}\n}\n\n/**\n * What viewing found about the goal's derived facts: a CYCLE (only an edit of runtimeEdges fixes it,\n * `--prepare` refuses it too) or field DIFFERENCES that `--prepare` would rewrite.\n */\nexport class RuntimeGoalDrift {\n constructor(\n public readonly cycle: string | null,\n public readonly differences: string[],\n ) {}\n}\n\n/** Loads, checks and (only on request) prepares the hand-authored runtime goal. */\nexport class RuntimeGoalFile {\n private readonly validator: RuntimeGoalValidator;\n\n constructor(public readonly goalPath: string = DEFAULT_RUNTIME_GOAL_PATH) {\n this.validator = new RuntimeGoalValidator(goalPath);\n }\n\n exists(workspaceRoot: string): boolean {\n return fs.existsSync(path.join(workspaceRoot, this.goalPath));\n }\n\n /** The goal as saved, after its shape, references and derived facts check out. Never writes anything. */\n load(workspaceRoot: string): RuntimeGraph {\n const parsed = this.structurallyValid(workspaceRoot);\n const derived = this.validator.derived(parsed);\n if (derived.length > 0)\n throw this.failure(`${this.goalPath} lacks derived facts the viewer needs:\\n ${derived.join('\\n ')}`, [\n new Option(`Compute them from the goal's relationships: ${RUNTIME_GOAL_PREPARE_COMMAND}`, true),\n this.editOption(false),\n ]);\n return parsed;\n }\n\n /**\n * Where the saved derived facts disagree with what the existing runtime level machinery\n * (RuntimeNodeLevels — the generator's own) computes from the goal's relationships. Reported, not\n * fixed: viewing is read-only, and `--prepare` is the explicit step that rewrites them.\n */\n drift(graph: RuntimeGraph): RuntimeGoalDrift {\n const recomputed = this.recompute(graph);\n if (typeof recomputed === 'string') return new RuntimeGoalDrift(recomputed, []);\n return new RuntimeGoalDrift(null, this.differences(graph, recomputed));\n }\n\n /**\n * Recompute levels, self-queue flags, contract queues and maxLevel, and WRITE the goal. Gated on\n * STRUCTURAL problems only: missing or stale derived facts are exactly what this step computes.\n */\n prepare(workspaceRoot: string): RuntimeGoalPreparation {\n const saved = this.structurallyValid(workspaceRoot);\n const recomputed = this.recompute(saved);\n if (typeof recomputed === 'string') throw this.failure(recomputed, [this.cycleOption()]);\n const changes = this.differences(saved, recomputed);\n const written = JSON.stringify(saved) !== JSON.stringify(recomputed);\n if (written) saveRuntimeGraph(recomputed, workspaceRoot, this.goalPath);\n return new RuntimeGoalPreparation(recomputed, changes, written);\n }\n\n /** The edit that clears a cycle verdict — `--prepare` cannot. */\n cycleOption(): Option {\n return new Option(`Break the cycle by editing \"runtimeEdges\" (or the queues it flows through) in ${this.goalPath}.`, true);\n }\n\n private structurallyValid(workspaceRoot: string): RuntimeGraph {\n if (!this.exists(workspaceRoot)) throw this.missing();\n let parsed: RuntimeGraph;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- hand-authored JSON decoding boundary adds the goal path and repair while preserving the cause\n try {\n parsed = JSON.parse(fs.readFileSync(path.join(workspaceRoot, this.goalPath), 'utf-8')) as RuntimeGraph;\n } catch (err: unknown) {\n const error = toError(err);\n throw this.failure(`${this.goalPath} is not valid JSON: ${error.message}`, [this.editOption(true)], error);\n }\n const problems = this.validator.structural(parsed);\n if (problems.length > 0)\n throw this.failure(`${this.goalPath} is not a usable runtime goal:\\n ${problems.join('\\n ')}`, [this.editOption(true)]);\n // A goal is hand-authored, so it may list one pair once per contract; every view draws ONE arrow per pair.\n parsed.runtimeEdges = new RuntimeEdgeMerge().merge(parsed.runtimeEdges);\n return parsed;\n }\n\n /** A deep copy with the generator's level machinery applied, or the cycle it refused. */\n private recompute(graph: RuntimeGraph): RuntimeGraph | string {\n const copy = JSON.parse(JSON.stringify(graph)) as RuntimeGraph;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- the shared level machinery throws its cycle verdict; the goal reports it against its own path\n try {\n new RuntimeNodeLevels().apply(copy);\n return copy;\n } catch (err: unknown) {\n const error = toError(err);\n if (!(error instanceof RuleFailError)) throw error;\n return `${this.goalPath} describes a runtime graph the level machinery cannot order: ${error.message}`;\n }\n }\n\n private differences(saved: RuntimeGraph, recomputed: RuntimeGraph): string[] {\n const lines: string[] = [];\n const level = (where: string, before: number | undefined, after: number): void => {\n if (before !== after) lines.push(`${where} level: L${before ?? '?'} → L${after}`);\n };\n for (const [name, service] of Object.entries(recomputed.services)) level(`services[\"${name}\"]`, saved.services[name].level, service.level);\n for (const [key, queue] of Object.entries(recomputed.queues)) {\n level(`queues[\"${key}\"]`, saved.queues[key].level, queue.level);\n if (saved.queues[key].selfQueue !== queue.selfQueue)\n lines.push(`queues[\"${key}\"] selfQueue: ${saved.queues[key].selfQueue} → ${queue.selfQueue}`);\n }\n lines.push(...this.contractDifferences(saved.contractQueues ?? {}, recomputed.contractQueues));\n recomputed.triggers.forEach((trigger: RuntimeTrigger, index: number) =>\n level(`triggers[${index}]`, saved.triggers[index].level, trigger.level),\n );\n if (saved.maxLevel !== recomputed.maxLevel) lines.push(`maxLevel: ${saved.maxLevel} → ${recomputed.maxLevel}`);\n return lines;\n }\n\n private contractDifferences(saved: Record<string, RuntimeQueueFlow>, recomputed: Record<string, RuntimeQueueFlow>): string[] {\n const lines: string[] = [];\n const fields = (flow: RuntimeQueueFlow | undefined): string =>\n flow === undefined\n ? '(absent)'\n : `L${flow.level} self=${flow.selfQueue} producers=[${flow.producedBy.join(', ')}] consumers=[${flow.consumedBy.join(', ')}]`;\n for (const api of [...new Set([...Object.keys(saved), ...Object.keys(recomputed)])].sort()) {\n const before = fields(saved[api]);\n const after = fields(recomputed[api]);\n if (before !== after) lines.push(`contractQueues[\"${api}\"]: ${before} → ${after}`);\n }\n return lines;\n }\n\n private missing(): RuleFailError {\n return this.failure(\n `No runtime goal at ${this.goalPath}. Viewing never creates or replaces the goal, and never renders the current graph in its place.`,\n [\n new Option(`Start a proposal from the saved current runtime graph: ${RUNTIME_GOAL_INIT_COMMAND} — then edit the copy and commit it.`, true),\n new Option(`Write ${this.goalPath} by hand in the RuntimeGraph shape of ${DEFAULT_RUNTIME_GRAPH_PATH}.`),\n ],\n );\n }\n\n private editOption(preferred: boolean): Option {\n return new Option(`Edit ${this.goalPath} by hand — it is a hand-authored proposal, so the fix is in the file, not in source.`, preferred);\n }\n\n private failure(message: string, options: Option[], cause?: Error): RuleFailError {\n return new RuleFailError(RUNTIME_GOAL_RULE_NAME, message, undefined, undefined, options, undefined, cause);\n }\n}\n\n/** What a goal page says it is: a hand-authored proposal, not a generated snapshot (#1239). */\nexport class RuntimeGoalProvenance {\n message(): string {\n return (\n `Goal / Proposed runtime architecture from ${DEFAULT_RUNTIME_GOAL_PATH}. Hand-authored; not generated from source, ` +\n 'and never written by architecture:generate. Viewing changes no file. ' +\n `After editing relationships, recompute derived levels explicitly: ${RUNTIME_GOAL_PREPARE_COMMAND}`\n );\n }\n\n html(): string {\n return (\n `<p class=\"hint\"><strong>Goal / Proposed runtime architecture.</strong> Hand-authored proposal read from ` +\n `<code>${DEFAULT_RUNTIME_GOAL_PATH}</code>; not generated from source and never written by ` +\n `<code>architecture:generate</code>. Viewing changes no file. After editing relationships, recompute derived ` +\n `levels explicitly: <code>${RUNTIME_GOAL_PREPARE_COMMAND}</code>. Compare with the saved current graph ` +\n `(<code>${DEFAULT_RUNTIME_GRAPH_PATH}</code>) in Color by → Goal vs Current.</p>`\n );\n }\n}\n"]}
@@ -11,8 +11,6 @@ import type { RuntimeGraph } from './runtime-graph-model';
11
11
  */
12
12
  export declare class RuntimeUnionGraph {
13
13
  build(current: RuntimeGraph, goal: RuntimeGraph): RuntimeGraph;
14
- /** Edges matched on (transport, from, to, queue); a matched edge carries both graphs' contracts. */
15
- private edges;
16
14
  private missing;
17
15
  private externals;
18
16
  private remotes;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RuntimeUnionGraph = void 0;
4
+ const runtime_edge_merge_1 = require("./runtime-edge-merge");
4
5
  /**
5
6
  * The OVERLAY drawn by Goal vs Current (#1239): every goal element, plus every element only the
6
7
  * current graph has, so removals stay visible instead of silently disappearing.
@@ -23,7 +24,8 @@ class RuntimeUnionGraph {
23
24
  union.queues[key] ??= queue;
24
25
  for (const [api, queue] of Object.entries(old.contractQueues))
25
26
  union.contractQueues[api] ??= queue;
26
- union.runtimeEdges = this.edges(union.runtimeEdges, old.runtimeEdges);
27
+ // Matched on (transport, from, to, queue); a matched edge carries both graphs' contracts.
28
+ union.runtimeEdges = new runtime_edge_merge_1.RuntimeEdgeMerge().merge([...union.runtimeEdges, ...old.runtimeEdges]);
27
29
  union.triggers = this.missing(union.triggers, old.triggers, (trigger) => `${trigger.kind} ${trigger.api}.${trigger.method} ${trigger.service}`);
28
30
  union.unresolvedUses = this.missing(union.unresolvedUses, old.unresolvedUses, (use) => `${use.service} ${use.api}`);
29
31
  this.externals(union, old);
@@ -31,24 +33,6 @@ class RuntimeUnionGraph {
31
33
  union.maxLevel = Math.max(union.maxLevel, old.maxLevel);
32
34
  return union;
33
35
  }
34
- /** Edges matched on (transport, from, to, queue); a matched edge carries both graphs' contracts. */
35
- edges(goal, current) {
36
- const key = (edge) => `${edge.type ?? 'rpc'} ${edge.from} ${edge.to} ${edge.queue ?? ''}`;
37
- const byKey = new Map();
38
- for (const edge of goal)
39
- byKey.set(key(edge), edge);
40
- const out = [...goal];
41
- for (const edge of current) {
42
- const match = byKey.get(key(edge));
43
- if (match === undefined) {
44
- byKey.set(key(edge), edge);
45
- out.push(edge);
46
- }
47
- else
48
- match.via = [...new Set([...match.via, ...edge.via])].sort();
49
- }
50
- return out;
51
- }
52
36
  missing(goal, current, key) {
53
37
  const seen = new Set(goal.map(key));
54
38
  return [...goal, ...current.filter((value) => !seen.has(key(value)))];
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-union-graph.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-union-graph.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n RuntimeEdge,\n RuntimeGraph,\n RuntimeRemoteCall,\n RuntimeTrigger,\n RuntimeUnresolved,\n} from './runtime-graph-model';\n\n/**\n * The OVERLAY drawn by Goal vs Current (#1239): every goal element, plus every element only the\n * current graph has, so removals stay visible instead of silently disappearing.\n *\n * Presentation only. It is never levelled, never cycle-checked and never saved: an overlay of two\n * architectures can contain a cycle neither input has, and that is not an architectural finding.\n * Every element keeps the level its OWN graph saved — retained and added elements sit at their goal\n * level, removed ones at their current level — so the shared renderer's strict level bands still\n * order the rows and only real upward edges become non-constraining feedback edges.\n */\nexport class RuntimeUnionGraph {\n build(current: RuntimeGraph, goal: RuntimeGraph): RuntimeGraph {\n const union = this.copy(goal);\n const old = this.copy(current);\n for (const [name, service] of Object.entries(old.services)) union.services[name] ??= service;\n for (const [name, api] of Object.entries(old.apis)) union.apis[name] ??= api;\n for (const [key, queue] of Object.entries(old.queues)) union.queues[key] ??= queue;\n for (const [api, queue] of Object.entries(old.contractQueues)) union.contractQueues[api] ??= queue;\n union.runtimeEdges = this.edges(union.runtimeEdges, old.runtimeEdges);\n union.triggers = this.missing(union.triggers, old.triggers, (trigger: RuntimeTrigger) => `${trigger.kind} ${trigger.api}.${trigger.method} ${trigger.service}`);\n union.unresolvedUses = this.missing(union.unresolvedUses, old.unresolvedUses, (use: RuntimeUnresolved) => `${use.service} ${use.api}`);\n this.externals(union, old);\n this.remotes(union, old);\n union.maxLevel = Math.max(union.maxLevel, old.maxLevel);\n return union;\n }\n\n /** Edges matched on (transport, from, to, queue); a matched edge carries both graphs' contracts. */\n private edges(goal: RuntimeEdge[], current: RuntimeEdge[]): RuntimeEdge[] {\n const key = (edge: RuntimeEdge): string => `${edge.type ?? 'rpc'} ${edge.from} ${edge.to} ${edge.queue ?? ''}`;\n const byKey = new Map<string, RuntimeEdge>();\n for (const edge of goal) byKey.set(key(edge), edge);\n const out = [...goal];\n for (const edge of current) {\n const match = byKey.get(key(edge));\n if (match === undefined) {\n byKey.set(key(edge), edge);\n out.push(edge);\n } else match.via = [...new Set([...match.via, ...edge.via])].sort();\n }\n return out;\n }\n\n private missing<T>(goal: T[], current: T[], key: (value: T) => string): T[] {\n const seen = new Set(goal.map(key));\n return [...goal, ...current.filter((value: T) => !seen.has(key(value)))];\n }\n\n private externals(union: RuntimeGraph, old: RuntimeGraph): void {\n if (union.externalSystems === undefined && old.externalSystems === undefined) return;\n const systems = (union.externalSystems ??= {});\n for (const [id, system] of Object.entries(old.externalSystems ?? {})) {\n const kept = systems[id];\n if (kept === undefined) {\n systems[id] = system;\n continue;\n }\n kept.usedBy = [...new Set([...kept.usedBy, ...system.usedBy])].sort();\n kept.apis = [...new Set([...kept.apis, ...system.apis])].sort();\n }\n }\n\n private remotes(union: RuntimeGraph, old: RuntimeGraph): void {\n if (union.remoteServices === undefined && old.remoteServices === undefined) return;\n const remotes = (union.remoteServices ??= {});\n for (const [name, remote] of Object.entries(old.remoteServices ?? {})) {\n const kept = remotes[name];\n if (kept === undefined) {\n remotes[name] = remote;\n continue;\n }\n for (const call of remote.calledBy) {\n const match = kept.calledBy.find((entry: RuntimeRemoteCall) => entry.from === call.from && entry.type === call.type);\n if (match === undefined) kept.calledBy.push(call);\n else match.via = [...new Set([...match.via, ...call.via])].sort();\n }\n }\n }\n\n private copy(graph: RuntimeGraph): RuntimeGraph {\n return JSON.parse(JSON.stringify(graph)) as RuntimeGraph;\n }\n}\n"]}
1
+ {"version":3,"file":"runtime-union-graph.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-union-graph.ts"],"names":[],"mappings":"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","sourcesContent":["import { RuntimeEdgeMerge } from './runtime-edge-merge';\nimport type {\n RuntimeGraph,\n RuntimeRemoteCall,\n RuntimeTrigger,\n RuntimeUnresolved,\n} from './runtime-graph-model';\n\n/**\n * The OVERLAY drawn by Goal vs Current (#1239): every goal element, plus every element only the\n * current graph has, so removals stay visible instead of silently disappearing.\n *\n * Presentation only. It is never levelled, never cycle-checked and never saved: an overlay of two\n * architectures can contain a cycle neither input has, and that is not an architectural finding.\n * Every element keeps the level its OWN graph saved — retained and added elements sit at their goal\n * level, removed ones at their current level — so the shared renderer's strict level bands still\n * order the rows and only real upward edges become non-constraining feedback edges.\n */\nexport class RuntimeUnionGraph {\n build(current: RuntimeGraph, goal: RuntimeGraph): RuntimeGraph {\n const union = this.copy(goal);\n const old = this.copy(current);\n for (const [name, service] of Object.entries(old.services)) union.services[name] ??= service;\n for (const [name, api] of Object.entries(old.apis)) union.apis[name] ??= api;\n for (const [key, queue] of Object.entries(old.queues)) union.queues[key] ??= queue;\n for (const [api, queue] of Object.entries(old.contractQueues)) union.contractQueues[api] ??= queue;\n // Matched on (transport, from, to, queue); a matched edge carries both graphs' contracts.\n union.runtimeEdges = new RuntimeEdgeMerge().merge([...union.runtimeEdges, ...old.runtimeEdges]);\n union.triggers = this.missing(union.triggers, old.triggers, (trigger: RuntimeTrigger) => `${trigger.kind} ${trigger.api}.${trigger.method} ${trigger.service}`);\n union.unresolvedUses = this.missing(union.unresolvedUses, old.unresolvedUses, (use: RuntimeUnresolved) => `${use.service} ${use.api}`);\n this.externals(union, old);\n this.remotes(union, old);\n union.maxLevel = Math.max(union.maxLevel, old.maxLevel);\n return union;\n }\n\n private missing<T>(goal: T[], current: T[], key: (value: T) => string): T[] {\n const seen = new Set(goal.map(key));\n return [...goal, ...current.filter((value: T) => !seen.has(key(value)))];\n }\n\n private externals(union: RuntimeGraph, old: RuntimeGraph): void {\n if (union.externalSystems === undefined && old.externalSystems === undefined) return;\n const systems = (union.externalSystems ??= {});\n for (const [id, system] of Object.entries(old.externalSystems ?? {})) {\n const kept = systems[id];\n if (kept === undefined) {\n systems[id] = system;\n continue;\n }\n kept.usedBy = [...new Set([...kept.usedBy, ...system.usedBy])].sort();\n kept.apis = [...new Set([...kept.apis, ...system.apis])].sort();\n }\n }\n\n private remotes(union: RuntimeGraph, old: RuntimeGraph): void {\n if (union.remoteServices === undefined && old.remoteServices === undefined) return;\n const remotes = (union.remoteServices ??= {});\n for (const [name, remote] of Object.entries(old.remoteServices ?? {})) {\n const kept = remotes[name];\n if (kept === undefined) {\n remotes[name] = remote;\n continue;\n }\n for (const call of remote.calledBy) {\n const match = kept.calledBy.find((entry: RuntimeRemoteCall) => entry.from === call.from && entry.type === call.type);\n if (match === undefined) kept.calledBy.push(call);\n else match.via = [...new Set([...match.via, ...call.via])].sort();\n }\n }\n }\n\n private copy(graph: RuntimeGraph): RuntimeGraph {\n return JSON.parse(JSON.stringify(graph)) as RuntimeGraph;\n }\n}\n"]}