@webpieces/nx-webpieces-rules 0.4.882 → 0.4.884

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +40 -13
  2. package/package.json +7 -7
  3. package/src/configs/tag-truth-configs.d.ts +15 -0
  4. package/src/configs/tag-truth-configs.js +20 -1
  5. package/src/configs/tag-truth-configs.js.map +1 -1
  6. package/src/executors/generate/executor.js +3 -0
  7. package/src/executors/generate/executor.js.map +1 -1
  8. package/src/index.d.ts +6 -3
  9. package/src/index.js +15 -1
  10. package/src/index.js.map +1 -1
  11. package/src/lib/graph-color-modes.d.ts +50 -6
  12. package/src/lib/graph-color-modes.js +88 -8
  13. package/src/lib/graph-color-modes.js.map +1 -1
  14. package/src/lib/graph-comparator.js +2 -0
  15. package/src/lib/graph-comparator.js.map +1 -1
  16. package/src/lib/graph-entry-levels.d.ts +35 -0
  17. package/src/lib/graph-entry-levels.js +60 -0
  18. package/src/lib/graph-entry-levels.js.map +1 -0
  19. package/src/lib/graph-impact.d.ts +5 -5
  20. package/src/lib/graph-impact.js +5 -5
  21. package/src/lib/graph-impact.js.map +1 -1
  22. package/src/lib/graph-legend.d.ts +11 -1
  23. package/src/lib/graph-legend.js +44 -6
  24. package/src/lib/graph-legend.js.map +1 -1
  25. package/src/lib/graph-level-bands.js +3 -1
  26. package/src/lib/graph-level-bands.js.map +1 -1
  27. package/src/lib/graph-loader.d.ts +7 -1
  28. package/src/lib/graph-loader.js +10 -1
  29. package/src/lib/graph-loader.js.map +1 -1
  30. package/src/lib/graph-metadata.d.ts +2 -0
  31. package/src/lib/graph-metadata.js +19 -0
  32. package/src/lib/graph-metadata.js.map +1 -1
  33. package/src/lib/graph-page-shell.d.ts +28 -6
  34. package/src/lib/graph-page-shell.js +55 -18
  35. package/src/lib/graph-page-shell.js.map +1 -1
  36. package/src/lib/graph-page-styles.js +3 -1
  37. package/src/lib/graph-page-styles.js.map +1 -1
  38. package/src/lib/graph-products.d.ts +79 -0
  39. package/src/lib/graph-products.js +199 -0
  40. package/src/lib/graph-products.js.map +1 -0
  41. package/src/lib/graph-render-model.d.ts +13 -2
  42. package/src/lib/graph-render-model.js +36 -4
  43. package/src/lib/graph-render-model.js.map +1 -1
  44. package/src/lib/graph-sorter.d.ts +13 -0
  45. package/src/lib/graph-sorter.js.map +1 -1
  46. package/src/lib/graph-visualizer.client.d.ts +20 -10
  47. package/src/lib/graph-visualizer.client.js +67 -31
  48. package/src/lib/graph-visualizer.client.js.map +1 -1
  49. package/src/lib/graph-visualizer.js +7 -6
  50. package/src/lib/graph-visualizer.js.map +1 -1
  51. package/src/lib/product-resolver.d.ts +38 -0
  52. package/src/lib/product-resolver.js +63 -0
  53. package/src/lib/product-resolver.js.map +1 -0
  54. package/src/lib/runtime-graph-model.d.ts +7 -0
  55. package/src/lib/runtime-graph-model.js.map +1 -1
  56. package/src/lib/runtime-graph.js +3 -0
  57. package/src/lib/runtime-graph.js.map +1 -1
  58. package/src/lib/runtime-html-page.d.ts +7 -0
  59. package/src/lib/runtime-html-page.js +29 -0
  60. package/src/lib/runtime-html-page.js.map +1 -1
  61. package/src/lib/runtime-visualizer.client.d.ts +7 -0
  62. package/src/lib/runtime-visualizer.client.js +32 -0
  63. package/src/lib/runtime-visualizer.client.js.map +1 -1
  64. package/src/lib/runtime-visualizer.js +3 -1
  65. package/src/lib/runtime-visualizer.js.map +1 -1
  66. package/src/lib/saved-snapshot.js +6 -2
  67. package/src/lib/saved-snapshot.js.map +1 -1
  68. package/src/lib/tag-truth.d.ts +20 -3
  69. package/src/lib/tag-truth.js +48 -7
  70. package/src/lib/tag-truth.js.map +1 -1
  71. package/src/nx-policy-registry.js +1 -0
  72. package/src/nx-policy-registry.js.map +1 -1
  73. package/src/rule-help.js +1 -0
  74. package/src/rule-help.js.map +1 -1
  75. package/src/rule-settings.js +12 -1
  76. package/src/rule-settings.js.map +1 -1
package/README.md CHANGED
@@ -116,9 +116,15 @@ popover, zoom floats bottom-right, and **Responsibilities** opens a right-hand p
116
116
  drawer footer.
117
117
 
118
118
  The graph answers one question at a time. Pick a mode from the drawer's **Color by** pulldown
119
- (the one place to switch) or with keys `1` `2` `3`; the choice is remembered per viewer and
120
- deep-linkable as `#runtime`, `#architecture` or `#impact`. Every box carries the same three
121
- lines in every mode: `L3 name` (level dimmed, name bold), its role, and every framework tag.
119
+ (the one place to switch) or with keys `1` `2` `3` `4`; the choice is remembered per viewer and
120
+ deep-linkable as `#runtime`, `#architecture`, `#impact` or `#product`. Every box carries the same
121
+ three lines in every mode: `L3 name` (level dimmed, name bold), its role, and every framework tag.
122
+
123
+ Every row is one level. A library's level is its dependency depth, but every server, client and
124
+ app (`role:server` / `role:client` / `role:app`) is pinned to the TOP row, one above the highest
125
+ library, so the top row is exactly the entry points (a `role:bundle` sits above the apps it
126
+ aggregates; a server orchestrator above the servers it boots). The `level` in `dependencies.json`
127
+ is that promoted level. The runtime graph keeps its call-depth levels.
122
128
 
123
129
  - **Runtime** — where the code can run. One runtime is a solid fill. One runtime plus a
124
130
  specialization (angular/react inside browser, express inside node) is nested: a frame in the
@@ -139,10 +145,10 @@ lines in every mode: `L3 name` (level dimmed, name bold), its role, and every f
139
145
  `git checkout <hash>` and run `pnpm arch:visualize`. `architecture:visualize` (no regenerate
140
146
  needed) scans every comparison that exists; `architecture:generate` refreshes only *Changed on
141
147
  this branch*, so on `main` it leaves Impact asking for `pnpm arch:visualize`. Projects that own a
142
- changed file, by nx's own file ownership, are solid amber ("touched"); the rest of the affected set is light amber
143
- ("affected — tests and build re-run"); every transitive dependency of the affected set that is
144
- not itself affected is light slate with a dashed border ("build input — compiled or restored from
145
- cache, unchanged"); everything else is grey. The drawer shows the counts per shade. The answer is per checkout, so
148
+ changed file, by nx's own file ownership, are solid amber ("changed"); the rest of the affected set is light amber
149
+ ("dependent — tests and build re-run"); every transitive dependency of the affected set that is
150
+ not itself affected is light slate with a dashed border ("dependency (built, unchanged)" — compiled
151
+ or restored from cache); everything else is grey ("not in this build"). The drawer shows the counts per shade. The answer is per checkout, so
146
152
  it is written to `architecture/.impact/dependencies.impact.js`, never into the committed html or
147
153
  json. That directory holds its own `.gitignore` (`*`), so a consuming repo needs no gitignore
148
154
  entry. When nx or git cannot answer, no sidecar is written and Impact is disabled with a
@@ -150,12 +156,26 @@ lines in every mode: `L3 name` (level dimmed, name bold), its role, and every f
150
156
  and legend only. When files changed but no project owns one (a lockfile, the workspace
151
157
  manifest), the legend names that workspace-global cause for the everything-affected result.
152
158
 
153
- **Filter** opens a popover of three groups that INTERSECT: the changes of the active Impact
154
- comparison, headed by its label and carrying the same branch / last-commit toggle (Everything ·
155
- Changed · Changed + what uses them (nx affected) · Changed + what they use · Whole build of this
156
- branch, each with its project count; disabled with Impact's reason when there is no Impact
157
- data), *Runtime* chips and *Role* chips (a box matches a group when it carries any selected
158
- chip). "Show N projects" applies it; active filters show as removable pills in the top bar and
159
+ - **Product** — which products it belongs to. A project declares a product with the nx tag
160
+ `product:<name>` (lowercase kebab, several allowed; the `product-tags` rule makes it required on
161
+ servers, clients and apps). A library is never tagged: it belongs to every product whose tagged
162
+ project reaches it over `dependsOn`, written per project as `"products": [...]` into
163
+ `dependencies.json`. One product is a solid fill in that product's color (a fixed palette of 8 in
164
+ sorted-name order; any product past 8 uses the neutral fill and the legend says so); two or more,
165
+ but not all, are vertical stripes, one per product; a box shared by every product is one neutral
166
+ fill; a box in no product is white with a dashed border. The legend counts each product's boxes
167
+ and how many belong to it alone.
168
+
169
+ **Filter** opens a popover of four groups that INTERSECT: the changes of the active Impact
170
+ comparison, headed by its label and carrying the same branch / last-commit toggle (All ·
171
+ Changed · Changed + dependents (+ everything that uses them: their tests re-run) · Changed +
172
+ dependencies (+ everything they use) · Changed + dependents + dependencies (everything CI builds for
173
+ this branch — the dependencies of the dependents too, so it is more than the union of the two rows
174
+ above it), each with its project count; disabled with Impact's reason when there is no Impact
175
+ data), *Runtime* chips, *Role* chips and *Product* chips (a box matches a group when it carries any
176
+ selected chip; several products select the union of their closures; the Product group is hidden when
177
+ nothing declares a product). *Product* ∩ *Changed + dependents + dependencies* is exactly what this
178
+ branch builds that can affect that product. "Show N projects" applies it; active filters show as removable pills in the top bar and
159
179
  a count badge on the drawer button. Every remaining box keeps its own L-row, a level left empty
160
180
  is a thin labeled band, and edges between remaining boxes stay drawn.
161
181
 
@@ -180,3 +200,10 @@ do not clear filtering, and no filter is persisted across reloads. Saved graph f
180
200
  and visibility options remain untouched. See
181
201
  [dependency graphs](../../../docs/architecture/dependency-graphs.md#filter-unconnected)
182
202
  for traversal examples and browser verification.
203
+
204
+ The runtime page also carries a **Product** chip row above the graph when any service belongs to a
205
+ product. A product's runtime services are its tagged services plus every service they reach over
206
+ runtime `dependsOn` (through a queue to its consumers too), so a call into another product's
207
+ service stays visible; queues, clocks and systems attached to a kept service stay with it. Several
208
+ chips keep the union, and the row intersects with Filter Unconnected. The page always opens
209
+ unfiltered. `runtime-dependencies.json` carries each service's `products`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/nx-webpieces-rules",
3
- "version": "0.4.882",
3
+ "version": "0.4.884",
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.882",
24
- "@webpieces/core-util": "0.4.882",
25
- "@webpieces/eslint-rules": "0.4.882",
26
- "@webpieces/rules-config": "0.4.882",
23
+ "@webpieces/api-doc-model": "0.4.884",
24
+ "@webpieces/core-util": "0.4.884",
25
+ "@webpieces/eslint-rules": "0.4.884",
26
+ "@webpieces/rules-config": "0.4.884",
27
27
  "madge": "8.0.0",
28
- "@webpieces/rules-sdk": "0.4.882"
28
+ "@webpieces/rules-sdk": "0.4.884"
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.882"
44
+ "@webpieces/tooling-testkit": "0.4.884"
45
45
  },
46
46
  "types": "./src/index.d.ts",
47
47
  "bin": {
@@ -70,3 +70,18 @@ export declare class FrameworkFolderConfig extends BaseRuleConfig {
70
70
  entries: FrameworkFolderEntry[];
71
71
  static readonly SCHEMA: SchemaShape<FrameworkFolderConfig>;
72
72
  }
73
+ /**
74
+ * `product-tags` (#1179) — every project whose role is in `roles` carries at least one well-formed
75
+ * `product:<name>` nx tag (lowercase kebab, several allowed). The product filter and the Product color
76
+ * mode of `arch:visualize` / `arch:visualize-runtime` seed a product's membership from exactly those
77
+ * tags, so an untagged server, client or app silently drops out of every product view.
78
+ *
79
+ * - `mode` — OFF | RUN_EVERY_TIME (a graph rule: it reads every project, every run).
80
+ * - `roles` — required, non-empty: WHICH roles are refused without a tag, e.g.
81
+ * `["server", "client", "app"]`. It decides which projects fail, so it is behaviour, never a default.
82
+ */
83
+ export declare class ProductTagsConfig extends BaseRuleConfig {
84
+ mode?: StructuralMode;
85
+ roles: string[];
86
+ static readonly SCHEMA: SchemaShape<ProductTagsConfig>;
87
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FrameworkFolderConfig = exports.FrameworkFolderEntry = exports.ApiLibPathConfig = exports.ApiLibDependenciesConfig = exports.ApiClientPackagesEntry = void 0;
3
+ exports.ProductTagsConfig = exports.FrameworkFolderConfig = exports.FrameworkFolderEntry = exports.ApiLibPathConfig = exports.ApiLibDependenciesConfig = exports.ApiClientPackagesEntry = void 0;
4
4
  const rules_sdk_1 = require("@webpieces/rules-sdk");
5
5
  const rule_configs_1 = require("./rule-configs");
6
6
  /** One `role:api-client` project and the outside packages it may import (its SDK, inversify, …). Data-only. */
@@ -95,4 +95,23 @@ class FrameworkFolderConfig extends rules_sdk_1.BaseRuleConfig {
95
95
  };
96
96
  }
97
97
  exports.FrameworkFolderConfig = FrameworkFolderConfig;
98
+ /**
99
+ * `product-tags` (#1179) — every project whose role is in `roles` carries at least one well-formed
100
+ * `product:<name>` nx tag (lowercase kebab, several allowed). The product filter and the Product color
101
+ * mode of `arch:visualize` / `arch:visualize-runtime` seed a product's membership from exactly those
102
+ * tags, so an untagged server, client or app silently drops out of every product view.
103
+ *
104
+ * - `mode` — OFF | RUN_EVERY_TIME (a graph rule: it reads every project, every run).
105
+ * - `roles` — required, non-empty: WHICH roles are refused without a tag, e.g.
106
+ * `["server", "client", "app"]`. It decides which projects fail, so it is behaviour, never a default.
107
+ */
108
+ class ProductTagsConfig extends rules_sdk_1.BaseRuleConfig {
109
+ roles;
110
+ static SCHEMA = {
111
+ mode: new rules_sdk_1.FieldDef('string', rule_configs_1.STRUCTURAL_MODES),
112
+ roles: rules_sdk_1.FieldDef.nonEmptyStrings(),
113
+ ...rules_sdk_1.BASE_RULE_SCHEMA,
114
+ };
115
+ }
116
+ exports.ProductTagsConfig = ProductTagsConfig;
98
117
  //# sourceMappingURL=tag-truth-configs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tag-truth-configs.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/configs/tag-truth-configs.ts"],"names":[],"mappings":";;;AAAA,oDAA+F;AAC/F,iDAAkE;AAClE,+GAA+G;AAC/G,MAAa,sBAAsB;IAC/B,iFAAiF;IACjF,OAAO,CAAU;IACjB,mGAAmG;IACnG,QAAQ,CAAY;IAEpB,MAAM,CAAU,MAAM,GAAwC;QAC1D,OAAO,EAAE,IAAI,oBAAQ,CAAC,QAAQ,CAAC;QAC/B,QAAQ,EAAE,IAAI,oBAAQ,CAAC,UAAU,CAAC;KACrC,CAAC;;AATN,wDAUC;AAED;;;;;;;;;;;GAWG;AACH,MAAa,wBAAyB,SAAQ,0BAAc;IAExD,cAAc,CAAY;IAC1B,UAAU,CAA4B;IAEtC,MAAM,CAAU,MAAM,GAA0C;QAC5D,IAAI,EAAE,IAAI,oBAAQ,CAAC,QAAQ,EAAE,+BAAgB,CAAC;QAC9C,cAAc,EAAE,IAAI,oBAAQ,CAAC,UAAU,CAAC;QACxC,UAAU,EAAE,oBAAQ,CAAC,OAAO,CAAC,sBAAsB,CAAC,MAAM,CAAC;QAC3D,GAAG,4BAAgB;KACtB,CAAC;;AAVN,4DAWC;AAED;;;;;;GAMG;AACH,MAAa,gBAAiB,SAAQ,0BAAc;IAEhD,KAAK,CAAY;IAEjB,MAAM,CAAU,MAAM,GAAkC;QACpD,IAAI,EAAE,IAAI,oBAAQ,CAAC,QAAQ,EAAE,+BAAgB,CAAC;QAC9C,KAAK,EAAE,oBAAQ,CAAC,eAAe,EAAE;QACjC,GAAG,4BAAgB;KACtB,CAAC;;AARN,4CASC;AAED;;;;;;GAMG;AACH,MAAa,oBAAoB;IAC7B,8EAA8E;IAC9E,KAAK,CAAY;IACjB,qGAAqG;IACrG,aAAa,CAAY;IACzB,qEAAqE;IACrE,KAAK,CAAY;IAEjB,MAAM,CAAU,MAAM,GAAsC;QACxD,KAAK,EAAE,oBAAQ,CAAC,eAAe,EAAE;QACjC,aAAa,EAAE,oBAAQ,CAAC,eAAe,EAAE;QACzC,KAAK,EAAE,oBAAQ,CAAC,eAAe,EAAE;KACpC,CAAC;;AAZN,oDAaC;AAED;;;;;;;;;;GAUG;AACH,MAAa,qBAAsB,SAAQ,0BAAc;IAErD,OAAO,CAA0B;IAEjC,MAAM,CAAU,MAAM,GAAuC;QACzD,IAAI,EAAE,IAAI,oBAAQ,CAAC,QAAQ,EAAE,+BAAgB,CAAC;QAC9C,OAAO,EAAE,oBAAQ,CAAC,eAAe,CAAC,oBAAoB,CAAC,MAAM,CAAC;QAC9D,GAAG,4BAAgB;KACtB,CAAC;;AARN,sDASC","sourcesContent":["import { FieldDef, SchemaShape, BaseRuleConfig, BASE_RULE_SCHEMA } from \"@webpieces/rules-sdk\";\nimport { StructuralMode, STRUCTURAL_MODES } from \"./rule-configs\";\n/** One `role:api-client` project and the outside packages it may import (its SDK, inversify, …). Data-only. */\nexport class ApiClientPackagesEntry {\n /** The nx project name of the `role:api-client` library, e.g. `gmail-client`. */\n project!: string;\n /** Package names (globs allowed, e.g. `@google-cloud/*`) it may import beyond `apiLibPackages`. */\n packages!: string[];\n\n static readonly SCHEMA: SchemaShape<ApiClientPackagesEntry> = {\n project: new FieldDef('string'),\n packages: new FieldDef('string[]'),\n };\n}\n\n/**\n * `api-lib-dependencies` (D2) — a `role:api-lib` may depend only on other `role:api-lib` projects plus\n * the outside packages in `apiLibPackages`; a `role:api-client` may ALSO import the packages its own\n * `apiClients` entry names (its SDK, `inversify`, `@webpieces/core-context`, …).\n *\n * - `mode` — OFF | RUN_EVERY_TIME (a graph rule: it reads every project, every run).\n * - `apiLibPackages` — required; may be empty (an api library then imports nothing from outside), e.g.\n * `[\"@webpieces/core-util\", \"tslib\"]`. A workspace project whose name or package name is listed here\n * is allowed too, so a repo whose `core-util` is a workspace project states it the same way.\n * - `apiClients` — required; one entry per `role:api-client` project. A `role:api-client` with no\n * entry is itself a violation: what it may talk to is stated, never inferred.\n */\nexport class ApiLibDependenciesConfig extends BaseRuleConfig {\n declare mode?: StructuralMode;\n apiLibPackages!: string[];\n apiClients!: ApiClientPackagesEntry[];\n\n static readonly SCHEMA: SchemaShape<ApiLibDependenciesConfig> = {\n mode: new FieldDef('string', STRUCTURAL_MODES),\n apiLibPackages: new FieldDef('string[]'),\n apiClients: FieldDef.objects(ApiClientPackagesEntry.SCHEMA),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n/**\n * `api-lib-path` (D10) — both directions: every `role:api-lib` / `role:api-client` project lives under\n * one of `paths`, and every project under `paths` carries one of those two roles.\n *\n * - `mode` — OFF | RUN_EVERY_TIME.\n * - `paths` — required, non-empty globs over project ROOTS, e.g. `[\"libraries/apis/**\"]`.\n */\nexport class ApiLibPathConfig extends BaseRuleConfig {\n declare mode?: StructuralMode;\n paths!: string[];\n\n static readonly SCHEMA: SchemaShape<ApiLibPathConfig> = {\n mode: new FieldDef('string', STRUCTURAL_MODES),\n paths: FieldDef.nonEmptyStrings(),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n/**\n * One folder of the runtime layout: the project roots it covers, the framework SETS a project there may\n * carry, and the roles it may have. Data-only.\n *\n * A framework set is written as its tags joined by `+`, in any order: `\"browser+node+react-native\"`.\n * Listing two sets means EITHER — `[\"node\", \"express\"]` is a node library or an express one.\n */\nexport class FrameworkFolderEntry {\n /** Globs over project ROOTS, e.g. `[\"libraries/universal/**\"]`. Non-empty. */\n paths!: string[];\n /** The accepted framework sets, each `+`-joined, e.g. `[\"browser+node+react-native\"]`. Non-empty. */\n frameworkSets!: string[];\n /** The accepted roles, e.g. `[\"lib\", \"designed-lib\"]`. Non-empty. */\n roles!: string[];\n\n static readonly SCHEMA: SchemaShape<FrameworkFolderEntry> = {\n paths: FieldDef.nonEmptyStrings(),\n frameworkSets: FieldDef.nonEmptyStrings(),\n roles: FieldDef.nonEmptyStrings(),\n };\n}\n\n/**\n * `framework-folder` (D9) — the folder a library lives in and the tags it carries say the same thing,\n * both directions:\n * - a project under an entry's `paths` (the FIRST entry whose glob matches, in `entries` order) must\n * carry one of that entry's `frameworkSets`, exactly, and one of its `roles`;\n * - a LIBRARY (any role but server/app/client/bundle) carrying a framework set and role that some entry\n * lists must live under one of the entries that list them.\n *\n * - `mode` — OFF | RUN_EVERY_TIME.\n * - `entries` — required, non-empty. The recommended layout is in `.claude/rules/framework-tags.md`.\n */\nexport class FrameworkFolderConfig extends BaseRuleConfig {\n declare mode?: StructuralMode;\n entries!: FrameworkFolderEntry[];\n\n static readonly SCHEMA: SchemaShape<FrameworkFolderConfig> = {\n mode: new FieldDef('string', STRUCTURAL_MODES),\n entries: FieldDef.nonEmptyObjects(FrameworkFolderEntry.SCHEMA),\n ...BASE_RULE_SCHEMA,\n };\n}\n"]}
1
+ {"version":3,"file":"tag-truth-configs.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/configs/tag-truth-configs.ts"],"names":[],"mappings":";;;AAAA,oDAA+F;AAC/F,iDAAkE;AAClE,+GAA+G;AAC/G,MAAa,sBAAsB;IAC/B,iFAAiF;IACjF,OAAO,CAAU;IACjB,mGAAmG;IACnG,QAAQ,CAAY;IAEpB,MAAM,CAAU,MAAM,GAAwC;QAC1D,OAAO,EAAE,IAAI,oBAAQ,CAAC,QAAQ,CAAC;QAC/B,QAAQ,EAAE,IAAI,oBAAQ,CAAC,UAAU,CAAC;KACrC,CAAC;;AATN,wDAUC;AAED;;;;;;;;;;;GAWG;AACH,MAAa,wBAAyB,SAAQ,0BAAc;IAExD,cAAc,CAAY;IAC1B,UAAU,CAA4B;IAEtC,MAAM,CAAU,MAAM,GAA0C;QAC5D,IAAI,EAAE,IAAI,oBAAQ,CAAC,QAAQ,EAAE,+BAAgB,CAAC;QAC9C,cAAc,EAAE,IAAI,oBAAQ,CAAC,UAAU,CAAC;QACxC,UAAU,EAAE,oBAAQ,CAAC,OAAO,CAAC,sBAAsB,CAAC,MAAM,CAAC;QAC3D,GAAG,4BAAgB;KACtB,CAAC;;AAVN,4DAWC;AAED;;;;;;GAMG;AACH,MAAa,gBAAiB,SAAQ,0BAAc;IAEhD,KAAK,CAAY;IAEjB,MAAM,CAAU,MAAM,GAAkC;QACpD,IAAI,EAAE,IAAI,oBAAQ,CAAC,QAAQ,EAAE,+BAAgB,CAAC;QAC9C,KAAK,EAAE,oBAAQ,CAAC,eAAe,EAAE;QACjC,GAAG,4BAAgB;KACtB,CAAC;;AARN,4CASC;AAED;;;;;;GAMG;AACH,MAAa,oBAAoB;IAC7B,8EAA8E;IAC9E,KAAK,CAAY;IACjB,qGAAqG;IACrG,aAAa,CAAY;IACzB,qEAAqE;IACrE,KAAK,CAAY;IAEjB,MAAM,CAAU,MAAM,GAAsC;QACxD,KAAK,EAAE,oBAAQ,CAAC,eAAe,EAAE;QACjC,aAAa,EAAE,oBAAQ,CAAC,eAAe,EAAE;QACzC,KAAK,EAAE,oBAAQ,CAAC,eAAe,EAAE;KACpC,CAAC;;AAZN,oDAaC;AAED;;;;;;;;;;GAUG;AACH,MAAa,qBAAsB,SAAQ,0BAAc;IAErD,OAAO,CAA0B;IAEjC,MAAM,CAAU,MAAM,GAAuC;QACzD,IAAI,EAAE,IAAI,oBAAQ,CAAC,QAAQ,EAAE,+BAAgB,CAAC;QAC9C,OAAO,EAAE,oBAAQ,CAAC,eAAe,CAAC,oBAAoB,CAAC,MAAM,CAAC;QAC9D,GAAG,4BAAgB;KACtB,CAAC;;AARN,sDASC;AAED;;;;;;;;;GASG;AACH,MAAa,iBAAkB,SAAQ,0BAAc;IAEjD,KAAK,CAAY;IAEjB,MAAM,CAAU,MAAM,GAAmC;QACrD,IAAI,EAAE,IAAI,oBAAQ,CAAC,QAAQ,EAAE,+BAAgB,CAAC;QAC9C,KAAK,EAAE,oBAAQ,CAAC,eAAe,EAAE;QACjC,GAAG,4BAAgB;KACtB,CAAC;;AARN,8CASC","sourcesContent":["import { FieldDef, SchemaShape, BaseRuleConfig, BASE_RULE_SCHEMA } from \"@webpieces/rules-sdk\";\nimport { StructuralMode, STRUCTURAL_MODES } from \"./rule-configs\";\n/** One `role:api-client` project and the outside packages it may import (its SDK, inversify, …). Data-only. */\nexport class ApiClientPackagesEntry {\n /** The nx project name of the `role:api-client` library, e.g. `gmail-client`. */\n project!: string;\n /** Package names (globs allowed, e.g. `@google-cloud/*`) it may import beyond `apiLibPackages`. */\n packages!: string[];\n\n static readonly SCHEMA: SchemaShape<ApiClientPackagesEntry> = {\n project: new FieldDef('string'),\n packages: new FieldDef('string[]'),\n };\n}\n\n/**\n * `api-lib-dependencies` (D2) — a `role:api-lib` may depend only on other `role:api-lib` projects plus\n * the outside packages in `apiLibPackages`; a `role:api-client` may ALSO import the packages its own\n * `apiClients` entry names (its SDK, `inversify`, `@webpieces/core-context`, …).\n *\n * - `mode` — OFF | RUN_EVERY_TIME (a graph rule: it reads every project, every run).\n * - `apiLibPackages` — required; may be empty (an api library then imports nothing from outside), e.g.\n * `[\"@webpieces/core-util\", \"tslib\"]`. A workspace project whose name or package name is listed here\n * is allowed too, so a repo whose `core-util` is a workspace project states it the same way.\n * - `apiClients` — required; one entry per `role:api-client` project. A `role:api-client` with no\n * entry is itself a violation: what it may talk to is stated, never inferred.\n */\nexport class ApiLibDependenciesConfig extends BaseRuleConfig {\n declare mode?: StructuralMode;\n apiLibPackages!: string[];\n apiClients!: ApiClientPackagesEntry[];\n\n static readonly SCHEMA: SchemaShape<ApiLibDependenciesConfig> = {\n mode: new FieldDef('string', STRUCTURAL_MODES),\n apiLibPackages: new FieldDef('string[]'),\n apiClients: FieldDef.objects(ApiClientPackagesEntry.SCHEMA),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n/**\n * `api-lib-path` (D10) — both directions: every `role:api-lib` / `role:api-client` project lives under\n * one of `paths`, and every project under `paths` carries one of those two roles.\n *\n * - `mode` — OFF | RUN_EVERY_TIME.\n * - `paths` — required, non-empty globs over project ROOTS, e.g. `[\"libraries/apis/**\"]`.\n */\nexport class ApiLibPathConfig extends BaseRuleConfig {\n declare mode?: StructuralMode;\n paths!: string[];\n\n static readonly SCHEMA: SchemaShape<ApiLibPathConfig> = {\n mode: new FieldDef('string', STRUCTURAL_MODES),\n paths: FieldDef.nonEmptyStrings(),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n/**\n * One folder of the runtime layout: the project roots it covers, the framework SETS a project there may\n * carry, and the roles it may have. Data-only.\n *\n * A framework set is written as its tags joined by `+`, in any order: `\"browser+node+react-native\"`.\n * Listing two sets means EITHER — `[\"node\", \"express\"]` is a node library or an express one.\n */\nexport class FrameworkFolderEntry {\n /** Globs over project ROOTS, e.g. `[\"libraries/universal/**\"]`. Non-empty. */\n paths!: string[];\n /** The accepted framework sets, each `+`-joined, e.g. `[\"browser+node+react-native\"]`. Non-empty. */\n frameworkSets!: string[];\n /** The accepted roles, e.g. `[\"lib\", \"designed-lib\"]`. Non-empty. */\n roles!: string[];\n\n static readonly SCHEMA: SchemaShape<FrameworkFolderEntry> = {\n paths: FieldDef.nonEmptyStrings(),\n frameworkSets: FieldDef.nonEmptyStrings(),\n roles: FieldDef.nonEmptyStrings(),\n };\n}\n\n/**\n * `framework-folder` (D9) — the folder a library lives in and the tags it carries say the same thing,\n * both directions:\n * - a project under an entry's `paths` (the FIRST entry whose glob matches, in `entries` order) must\n * carry one of that entry's `frameworkSets`, exactly, and one of its `roles`;\n * - a LIBRARY (any role but server/app/client/bundle) carrying a framework set and role that some entry\n * lists must live under one of the entries that list them.\n *\n * - `mode` — OFF | RUN_EVERY_TIME.\n * - `entries` — required, non-empty. The recommended layout is in `.claude/rules/framework-tags.md`.\n */\nexport class FrameworkFolderConfig extends BaseRuleConfig {\n declare mode?: StructuralMode;\n entries!: FrameworkFolderEntry[];\n\n static readonly SCHEMA: SchemaShape<FrameworkFolderConfig> = {\n mode: new FieldDef('string', STRUCTURAL_MODES),\n entries: FieldDef.nonEmptyObjects(FrameworkFolderEntry.SCHEMA),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n/**\n * `product-tags` (#1179) — every project whose role is in `roles` carries at least one well-formed\n * `product:<name>` nx tag (lowercase kebab, several allowed). The product filter and the Product color\n * mode of `arch:visualize` / `arch:visualize-runtime` seed a product's membership from exactly those\n * tags, so an untagged server, client or app silently drops out of every product view.\n *\n * - `mode` — OFF | RUN_EVERY_TIME (a graph rule: it reads every project, every run).\n * - `roles` — required, non-empty: WHICH roles are refused without a tag, e.g.\n * `[\"server\", \"client\", \"app\"]`. It decides which projects fail, so it is behaviour, never a default.\n */\nexport class ProductTagsConfig extends BaseRuleConfig {\n declare mode?: StructuralMode;\n roles!: string[];\n\n static readonly SCHEMA: SchemaShape<ProductTagsConfig> = {\n mode: new FieldDef('string', STRUCTURAL_MODES),\n roles: FieldDef.nonEmptyStrings(),\n ...BASE_RULE_SCHEMA,\n };\n}\n"]}
@@ -134,6 +134,9 @@ class ArchitectureGenerator {
134
134
  console.log('🏷️ Enriching graph with framework + responsibilities metadata...');
135
135
  const projectInfos = await (0, graph_metadata_1.collectProjectInfo)();
136
136
  (0, graph_metadata_1.enrichGraph)(enhancedGraph, projectInfos, workspaceRoot);
137
+ // enrichGraph pinned servers, clients and apps to the top row (#1179): prove the promotion
138
+ // kept every dependency strictly below its dependent before anything is written.
139
+ cycles.assertLevelsDescend(reducedGraph, cycles.levelsOf(enhancedGraph), 'the entry-point-promoted graph');
137
140
  new tag_truth_1.TagTruthCheck().assertTrue(enhancedGraph, projectInfos, workspaceRoot); // #1064, before any write
138
141
  // Step 3b: Classify each api-lib edge (implements/uses + rpc/pubsub) by
139
142
  // scanning source, so dependencies.json + the viz + the runtime graph all
@@ -1 +1 @@
1
- {"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/generate/executor.ts"],"names":[],"mappings":"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","sourcesContent":["import { ApprovedWiringGraph } from '../../lib/runtime-wiring/approved-graph';\nimport { loadBlessedGraph } from '../../lib/graph-loader';\n/**\n * Generate Executor\n *\n * Generates the architecture dependency graph and saves it to architecture/dependencies.json, plus\n * one contract file per API under architecture/apis/ (stale ones are deleted).\n *\n * Usage:\n * nx run architecture:generate\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport {\n writeTemplate,\n Option,\n RuleFailError,\n renderRuleFailForHuman,\n} from '@webpieces/rules-config';\nimport { generateReducedGraph } from '../../lib/graph-generator';\nimport { sortGraphTopologically } from '../../lib/graph-sorter';\nimport { ProjectCycleDetector } from '../../lib/graph-cycles';\nimport { saveGraph, DEFAULT_GRAPH_PATH } from '../../lib/graph-loader';\nimport { ApiContractFiles } from '../../lib/api-contract-files';\nimport { collectProjectInfo, enrichGraph, MetadataValidationError } from '../../lib/graph-metadata';\nimport { TagTruthCheck } from '../../lib/tag-truth';\nimport { ProjectInfo } from '../../lib/project-info';\nimport type { ApiContracts, ExternalSystemDecls } from '../../lib/api-usage/api-relations';\nimport type { EnhancedGraph, GraphEntry } from '../../lib/graph-sorter';\nimport { GraphVisualizer } from '../../lib/graph-visualizer';\nimport { GENERATE_IMPACT_KINDS, ImpactRefresh } from '../../lib/graph-impact';\nimport * as path from 'path';\nimport { deriveRuntimeGraphReport, saveRuntimeGraph } from '../../lib/runtime-graph';\nimport { printAutoHiddenServers } from '../../lib/runtime-participant-resolver';\nimport { toError } from '../../toError';\n\nexport interface GenerateExecutorOptions {\n graphPath?: string;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\n/**\n * Generate the runtime microservice graph alongside the compile-time graph, DERIVED from the same\n * dependencies.json (its per-project apiRelations) — one regenerate produces both committed files, and\n * validate derives from the SAME source so they can't diverge. rpc APIs become direct runtime edges;\n * pubsub APIs become edges the viz draws through a queue.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction generateRuntimeGraph(\n workspaceRoot: string,\n graph: EnhancedGraph,\n hiddenProjects: Set<string>,\n apiContracts: ApiContracts,\n externalSystems: ExternalSystemDecls,\n): void {\n console.log('📡 Deriving runtime graph from dependencies.json (implements × uses per API)...');\n const report = deriveRuntimeGraphReport(graph, hiddenProjects, apiContracts, externalSystems);\n saveRuntimeGraph(report.graph, workspaceRoot);\n const serviceCount = Object.keys(report.graph.services).length;\n const queueCount = Object.keys(report.graph.queues).length;\n console.log(\n `✅ Runtime graph saved (${serviceCount} services, ${report.graph.runtimeEdges.length} runtime edges, ` +\n `${queueCount} queues, ${report.graph.triggers.length} cron/external triggers)`,\n );\n // Every edge the derivation had to GUESS at. Loud on purpose: a fanned-out edge is committed and\n // then reasoned about as if it were derived, which is how a fictional call survives for months.\n printRuntimeWarnings(report.warnings);\n printAutoHiddenServers(report.autoHidden);\n // Printed here, FAILED by validate-runtime-architecture: generate must still write the graph,\n // or the validator would have nothing to compare against and the error would be unfixable.\n if (report.problems.length > 0) {\n console.error(\n `❌ ${report.problems.length} client call(s) name a service no module answers to:`,\n );\n for (const problem of report.problems) console.error(` • ${problem}`);\n console.error(' This FAILS architecture:validate-runtime-architecture.');\n }\n}\n\n/** Print the derivation's guessed-edge warnings (nothing at all when the graph is fully targeted). */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction printRuntimeWarnings(warnings: string[]): void {\n if (warnings.length === 0) return;\n console.warn(`⚠️ ${warnings.length} runtime edge(s) could not be targeted to ONE service:`);\n for (const warning of warnings) console.warn(` • ${warning}`);\n}\n\n/**\n * Scan for api relations and attach them, surfacing any contract we could NOT map back to source.\n * Unresolved contracts mean the graph we are about to WRITE is incomplete, so they must be loud —\n * a green run that quietly omits a service gets committed, rendered, and trusted.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction scanApiRelations(\n workspaceRoot: string,\n graph: EnhancedGraph,\n projectInfos: Map<string, ProjectInfo>,\n graphPath: string = DEFAULT_GRAPH_PATH,\n): ScannedTables {\n console.log('📖 Assembling approved runtime-deps.json declarations (no source extraction)...');\n new ApprovedWiringGraph().attach(workspaceRoot, graph, projectInfos, graphPath);\n const saved = loadBlessedGraph(workspaceRoot, graphPath);\n if (saved === null)\n throw new RuleFailError(\n 'validate-runtime-architecture',\n 'Missing approved API contract references.',\n undefined,\n undefined,\n [\n new Option(\n 'Migrate src/wiring.ts and review runtime-deps.json and API contract approvals before generation.',\n true,\n ),\n ],\n );\n return new ScannedTables(\n new ApiContractFiles().load(workspaceRoot, graphPath, saved.apiContractFiles),\n saved.externalSystems,\n );\n}\n\n/** The two committed tables one scan produces, kept together so callers cannot persist just one. */\nclass ScannedTables {\n constructor(\n public readonly apiContracts: ApiContracts,\n public readonly externalSystems: ExternalSystemDecls,\n ) {}\n}\n\n/** Projects tagged drawOnGraph:false — kept in the JSON, omitted from every rendered graph. */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction hiddenProjectsIn(graph: EnhancedGraph): Set<string> {\n const hidden = new Set<string>();\n for (const name of Object.keys(graph)) {\n if (graph[name].drawOnGraph === false) hidden.add(name);\n }\n return hidden;\n}\n\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction printGraphSummary(graph: EnhancedGraph): void {\n const levels = new Set(Object.values(graph).map((entry: GraphEntry) => entry.level));\n console.log(`\\n📈 Graph Summary:`);\n console.log(` Projects: ${Object.keys(graph).length}`);\n console.log(` Levels: ${levels.size} (0-${Math.max(...levels)})`);\n}\n\n/**\n * The whole generation pipeline. Split out of runExecutor so that function stays a thin\n * try/catch-and-report shell — the error reporting is what a caller reads on failure, and it should\n * not be separated from the throw by fifty lines of steps.\n */\nexport class ArchitectureGenerator {\n constructor(\n private readonly visualizer: GraphVisualizer,\n private readonly impact: ImpactRefresh = new ImpactRefresh(),\n ) {}\n\n async generate(workspaceRoot: string, graphPath: string | undefined): Promise<void> {\n // Step 1: Build the full graph from nx, then transitively reduce it to the view\n console.log(\"📊 Generating dependency graph from nx's project graph...\");\n const reducedGraph = await generateReducedGraph();\n\n // Step 1b: The graph is a BUILD graph — refuse a cyclic one, naming EVERY cycle. This runs\n // before the sort deliberately: the sort also refuses, but reports one cycle and an\n // undifferentiated list of everything tangled with it, so a repo with several cycles pays one\n // full regeneration per cycle to discover them.\n console.log('🔄 Checking the project graph is acyclic...');\n const cycles = new ProjectCycleDetector();\n cycles.assertAcyclic(reducedGraph, 'the nx project graph');\n\n // Step 2: Topological sort (to assign levels for visualization)\n console.log('🔄 Computing topological layers...');\n const enhancedGraph = sortGraphTopologically(reducedGraph);\n // ...and assert the stratification it just produced actually holds: every dependency strictly\n // below its dependent. Safe to assert only because the graph was sorted a line ago — a stale\n // committed file is never checked this way.\n cycles.assertLevelsDescend(\n reducedGraph,\n cycles.levelsOf(enhancedGraph),\n 'the freshly sorted graph',\n );\n\n // Step 3: Enrich with AI metadata (framework, shortDescription, file\n // pointers). This VALIDATES (responsibilities.md required per project)\n // and throws before any write, so a failure never clobbers the file.\n console.log('🏷️ Enriching graph with framework + responsibilities metadata...');\n const projectInfos = await collectProjectInfo();\n enrichGraph(enhancedGraph, projectInfos, workspaceRoot);\n new TagTruthCheck().assertTrue(enhancedGraph, projectInfos, workspaceRoot); // #1064, before any write\n\n // Step 3b: Classify each api-lib edge (implements/uses + rpc/pubsub) by\n // scanning source, so dependencies.json + the viz + the runtime graph all\n // read the same derived truth.\n const scanned = scanApiRelations(workspaceRoot, enhancedGraph, projectInfos, graphPath);\n const apiContracts = scanned.apiContracts;\n\n saveApprovedGraph(workspaceRoot, graphPath ?? DEFAULT_GRAPH_PATH, enhancedGraph, scanned);\n\n // Step 4b: Write the committed, clickable HTML view next to the JSON so\n // dependencies.html regenerates in lock-step with dependencies.json.\n const vizPaths = this.visualizer.writeVisualization(enhancedGraph, workspaceRoot);\n console.log(`✅ Wrote ${vizPaths.htmlPath}`);\n\n // Step 4c: The page's Impact mode — nx affected for \"Changed on this branch\" ONLY, into a\n // gitignored sidecar beside the html (never into the committed files). Generate never scans\n // the last commit; that comparison is arch:visualize's. Optional: it reports, it never throws.\n console.log(\n await this.impact.run(path.dirname(vizPaths.htmlPath), workspaceRoot, enhancedGraph, GENERATE_IMPACT_KINDS),\n );\n\n // Step 5: Generate the runtime microservice graph from the same scan.\n // Projects tagged drawOnGraph:false are threaded through so the runtime\n // graph hides them too (they stay flagged in runtime-dependencies.json).\n generateRuntimeGraph(\n workspaceRoot,\n enhancedGraph,\n hiddenProjectsIn(enhancedGraph),\n apiContracts,\n scanned.externalSystems,\n );\n\n printGraphSummary(enhancedGraph);\n }\n}\n\n// webpieces-disable no-function-outside-class -- persist the approved graph and contract tables together\nfunction saveApprovedGraph(\n workspaceRoot: string,\n effectiveGraphPath: string,\n enhancedGraph: EnhancedGraph,\n scanned: ScannedTables,\n): void {\n // Step 4: Write one contract file per API (architecture/apis/<Api>.json) and delete the files of\n // APIs that no longer exist. dependencies.json only LINKS to them, so an endpoint change rewrites\n // an api file and never the dependency graph. The runtime validator reads these files back.\n const apiContracts = scanned.apiContracts;\n const contractFiles = new ApiContractFiles();\n const written = contractFiles.write(workspaceRoot, effectiveGraphPath, apiContracts);\n console.log(`✅ Wrote ${written.written.length} api contract file(s)`);\n for (const deleted of written.deleted)\n console.log(`🗑️ Deleted stale api contract file ${deleted}`);\n\n // Step 4a: Save the graph, INCLUDING the contract-file links and the external-system\n // declarations the runtime derivation reads back — generate derives from the in-memory graph,\n // validate from the files, so anything not written here would make the two disagree.\n console.log('💾 Saving graph to architecture/dependencies.json...');\n const contractRefs = contractFiles.refsFor(apiContracts);\n saveGraph(\n enhancedGraph,\n workspaceRoot,\n effectiveGraphPath,\n contractRefs,\n scanned.externalSystems,\n );\n console.log('✅ Graph saved successfully');\n}\n\nexport default async function runExecutor(\n options: GenerateExecutorOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n const graphPath = options.graphPath;\n const workspaceRoot = context.root;\n\n console.log('\\n📊 Architecture Graph Generator\\n');\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n await new ArchitectureGenerator(new GraphVisualizer()).generate(workspaceRoot, graphPath);\n return { success: true };\n } catch (err: unknown) {\n const error = toError(err);\n // A RuleFailError carries its cures as Option[]; `Error.message` is only the aiMessage, so\n // printing that alone silently drops them. renderRuleFailForHuman is the ONE renderer that\n // numbers them, and this catch is the top-level handler for the nx target.\n const rendered =\n error instanceof RuleFailError ? renderRuleFailForHuman(error) : error.message;\n console.error('❌ Graph generation failed:', rendered);\n if (error instanceof MetadataValidationError) {\n const mdPath = writeTemplate(workspaceRoot, 'webpieces.responsibilities.md');\n console.error('');\n console.error(\n '⚠️ *** Refer to ' +\n mdPath +\n ' for how to author responsibilities.md files *** ⚠️',\n );\n }\n return { success: false };\n }\n}\n"]}
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/generate/executor.ts"],"names":[],"mappings":";;;AAwQA,8BAgCC;;AAxSD,4EAA8E;AAC9E,yDAA0D;AAY1D,0DAKiC;AACjC,+DAAiE;AACjE,yDAAgE;AAChE,yDAA8D;AAC9D,yDAAuE;AACvE,qEAAgE;AAChE,6DAAoG;AACpG,mDAAoD;AAIpD,iEAA6D;AAC7D,yDAA8E;AAC9E,mDAA6B;AAC7B,2DAAqF;AACrF,yFAAgF;AAChF,2CAAwC;AAUxC;;;;;GAKG;AACH,2GAA2G;AAC3G,SAAS,oBAAoB,CACzB,aAAqB,EACrB,KAAoB,EACpB,cAA2B,EAC3B,YAA0B,EAC1B,eAAoC;IAEpC,OAAO,CAAC,GAAG,CAAC,iFAAiF,CAAC,CAAC;IAC/F,MAAM,MAAM,GAAG,IAAA,wCAAwB,EAAC,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,CAAC,CAAC;IAC9F,IAAA,gCAAgB,EAAC,MAAM,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IAC9C,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC;IAC/D,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;IAC3D,OAAO,CAAC,GAAG,CACP,0BAA0B,YAAY,cAAc,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,kBAAkB;QAClG,GAAG,UAAU,YAAY,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,0BAA0B,CACtF,CAAC;IACF,iGAAiG;IACjG,gGAAgG;IAChG,oBAAoB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACtC,IAAA,qDAAsB,EAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC1C,8FAA8F;IAC9F,2FAA2F;IAC3F,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,OAAO,CAAC,KAAK,CACT,KAAK,MAAM,CAAC,QAAQ,CAAC,MAAM,sDAAsD,CACpF,CAAC;QACF,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ;YAAE,OAAO,CAAC,KAAK,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC;QAC1E,OAAO,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC;IAC/E,CAAC;AACL,CAAC;AAED,sGAAsG;AACtG,2GAA2G;AAC3G,SAAS,oBAAoB,CAAC,QAAkB;IAC5C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAClC,OAAO,CAAC,IAAI,CAAC,OAAO,QAAQ,CAAC,MAAM,wDAAwD,CAAC,CAAC;IAC7F,KAAK,MAAM,OAAO,IAAI,QAAQ;QAAE,OAAO,CAAC,IAAI,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC;AACtE,CAAC;AAED;;;;GAIG;AACH,2GAA2G;AAC3G,SAAS,gBAAgB,CACrB,aAAqB,EACrB,KAAoB,EACpB,YAAsC,EACtC,YAAoB,iCAAkB;IAEtC,OAAO,CAAC,GAAG,CAAC,iFAAiF,CAAC,CAAC;IAC/F,IAAI,oCAAmB,EAAE,CAAC,MAAM,CAAC,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;IAChF,MAAM,KAAK,GAAG,IAAA,+BAAgB,EAAC,aAAa,EAAE,SAAS,CAAC,CAAC;IACzD,IAAI,KAAK,KAAK,IAAI;QACd,MAAM,IAAI,4BAAa,CACnB,+BAA+B,EAC/B,2CAA2C,EAC3C,SAAS,EACT,SAAS,EACT;YACI,IAAI,qBAAM,CACN,kGAAkG,EAClG,IAAI,CACP;SACJ,CACJ,CAAC;IACN,OAAO,IAAI,aAAa,CACpB,IAAI,qCAAgB,EAAE,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,EAAE,KAAK,CAAC,gBAAgB,CAAC,EAC7E,KAAK,CAAC,eAAe,CACxB,CAAC;AACN,CAAC;AAED,oGAAoG;AACpG,MAAM,aAAa;IAEK;IACA;IAFpB,YACoB,YAA0B,EAC1B,eAAoC;QADpC,iBAAY,GAAZ,YAAY,CAAc;QAC1B,oBAAe,GAAf,eAAe,CAAqB;IACrD,CAAC;CACP;AAED,+FAA+F;AAC/F,2GAA2G;AAC3G,SAAS,gBAAgB,CAAC,KAAoB;IAC1C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACpC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,WAAW,KAAK,KAAK;YAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED,2GAA2G;AAC3G,SAAS,iBAAiB,CAAC,KAAoB;IAC3C,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,KAAiB,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IACrF,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IACnC,OAAO,CAAC,GAAG,CAAC,gBAAgB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,IAAI,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;AACxE,CAAC;AAED;;;;GAIG;AACH,MAAa,qBAAqB;IAET;IACA;IAFrB,YACqB,UAA2B,EAC3B,SAAwB,IAAI,4BAAa,EAAE;QAD3C,eAAU,GAAV,UAAU,CAAiB;QAC3B,WAAM,GAAN,MAAM,CAAqC;IAC7D,CAAC;IAEJ,KAAK,CAAC,QAAQ,CAAC,aAAqB,EAAE,SAA6B;QAC/D,gFAAgF;QAChF,OAAO,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC;QACzE,MAAM,YAAY,GAAG,MAAM,IAAA,sCAAoB,GAAE,CAAC;QAElD,2FAA2F;QAC3F,oFAAoF;QACpF,8FAA8F;QAC9F,gDAAgD;QAChD,OAAO,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC;QAC3D,MAAM,MAAM,GAAG,IAAI,mCAAoB,EAAE,CAAC;QAC1C,MAAM,CAAC,aAAa,CAAC,YAAY,EAAE,sBAAsB,CAAC,CAAC;QAE3D,gEAAgE;QAChE,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;QAClD,MAAM,aAAa,GAAG,IAAA,qCAAsB,EAAC,YAAY,CAAC,CAAC;QAC3D,8FAA8F;QAC9F,6FAA6F;QAC7F,4CAA4C;QAC5C,MAAM,CAAC,mBAAmB,CACtB,YAAY,EACZ,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,EAC9B,0BAA0B,CAC7B,CAAC;QAEF,qEAAqE;QACrE,uEAAuE;QACvE,qEAAqE;QACrE,OAAO,CAAC,GAAG,CAAC,oEAAoE,CAAC,CAAC;QAClF,MAAM,YAAY,GAAG,MAAM,IAAA,mCAAkB,GAAE,CAAC;QAChD,IAAA,4BAAW,EAAC,aAAa,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;QACxD,2FAA2F;QAC3F,iFAAiF;QACjF,MAAM,CAAC,mBAAmB,CAAC,YAAY,EAAE,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,gCAAgC,CAAC,CAAC;QAC3G,IAAI,yBAAa,EAAE,CAAC,UAAU,CAAC,aAAa,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC,0BAA0B;QAEtG,wEAAwE;QACxE,0EAA0E;QAC1E,+BAA+B;QAC/B,MAAM,OAAO,GAAG,gBAAgB,CAAC,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;QACxF,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;QAE1C,iBAAiB,CAAC,aAAa,EAAE,SAAS,IAAI,iCAAkB,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;QAE1F,wEAAwE;QACxE,qEAAqE;QACrE,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,kBAAkB,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;QAClF,OAAO,CAAC,GAAG,CAAC,WAAW,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;QAE5C,0FAA0F;QAC1F,4FAA4F;QAC5F,+FAA+F;QAC/F,OAAO,CAAC,GAAG,CACP,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,EAAE,aAAa,EAAE,oCAAqB,CAAC,CAC9G,CAAC;QAEF,sEAAsE;QACtE,wEAAwE;QACxE,yEAAyE;QACzE,oBAAoB,CAChB,aAAa,EACb,aAAa,EACb,gBAAgB,CAAC,aAAa,CAAC,EAC/B,YAAY,EACZ,OAAO,CAAC,eAAe,CAC1B,CAAC;QAEF,iBAAiB,CAAC,aAAa,CAAC,CAAC;IACrC,CAAC;CACJ;AA3ED,sDA2EC;AAED,yGAAyG;AACzG,SAAS,iBAAiB,CACtB,aAAqB,EACrB,kBAA0B,EAC1B,aAA4B,EAC5B,OAAsB;IAEtB,iGAAiG;IACjG,kGAAkG;IAClG,4FAA4F;IAC5F,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;IAC1C,MAAM,aAAa,GAAG,IAAI,qCAAgB,EAAE,CAAC;IAC7C,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,aAAa,EAAE,kBAAkB,EAAE,YAAY,CAAC,CAAC;IACrF,OAAO,CAAC,GAAG,CAAC,WAAW,OAAO,CAAC,OAAO,CAAC,MAAM,uBAAuB,CAAC,CAAC;IACtE,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,OAAO;QACjC,OAAO,CAAC,GAAG,CAAC,wCAAwC,OAAO,EAAE,CAAC,CAAC;IAEnE,qFAAqF;IACrF,8FAA8F;IAC9F,qFAAqF;IACrF,OAAO,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAC;IACpE,MAAM,YAAY,GAAG,aAAa,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACzD,IAAA,wBAAS,EACL,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,OAAO,CAAC,eAAe,CAC1B,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;AAC9C,CAAC;AAEc,KAAK,UAAU,WAAW,CACrC,OAAgC,EAChC,OAAwB;IAExB,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IACpC,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAEnC,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;IAEnD,8DAA8D;IAC9D,IAAI,CAAC;QACD,MAAM,IAAI,qBAAqB,CAAC,IAAI,kCAAe,EAAE,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;QAC1F,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,IAAA,iBAAO,EAAC,GAAG,CAAC,CAAC;QAC3B,2FAA2F;QAC3F,2FAA2F;QAC3F,2EAA2E;QAC3E,MAAM,QAAQ,GACV,KAAK,YAAY,4BAAa,CAAC,CAAC,CAAC,IAAA,qCAAsB,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;QACnF,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,QAAQ,CAAC,CAAC;QACtD,IAAI,KAAK,YAAY,wCAAuB,EAAE,CAAC;YAC3C,MAAM,MAAM,GAAG,IAAA,4BAAa,EAAC,aAAa,EAAE,+BAA+B,CAAC,CAAC;YAC7E,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAClB,OAAO,CAAC,KAAK,CACT,mBAAmB;gBACf,MAAM;gBACN,qDAAqD,CAC5D,CAAC;QACN,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;AACL,CAAC","sourcesContent":["import { ApprovedWiringGraph } from '../../lib/runtime-wiring/approved-graph';\nimport { loadBlessedGraph } from '../../lib/graph-loader';\n/**\n * Generate Executor\n *\n * Generates the architecture dependency graph and saves it to architecture/dependencies.json, plus\n * one contract file per API under architecture/apis/ (stale ones are deleted).\n *\n * Usage:\n * nx run architecture:generate\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport {\n writeTemplate,\n Option,\n RuleFailError,\n renderRuleFailForHuman,\n} from '@webpieces/rules-config';\nimport { generateReducedGraph } from '../../lib/graph-generator';\nimport { sortGraphTopologically } from '../../lib/graph-sorter';\nimport { ProjectCycleDetector } from '../../lib/graph-cycles';\nimport { saveGraph, DEFAULT_GRAPH_PATH } from '../../lib/graph-loader';\nimport { ApiContractFiles } from '../../lib/api-contract-files';\nimport { collectProjectInfo, enrichGraph, MetadataValidationError } from '../../lib/graph-metadata';\nimport { TagTruthCheck } from '../../lib/tag-truth';\nimport { ProjectInfo } from '../../lib/project-info';\nimport type { ApiContracts, ExternalSystemDecls } from '../../lib/api-usage/api-relations';\nimport type { EnhancedGraph, GraphEntry } from '../../lib/graph-sorter';\nimport { GraphVisualizer } from '../../lib/graph-visualizer';\nimport { GENERATE_IMPACT_KINDS, ImpactRefresh } from '../../lib/graph-impact';\nimport * as path from 'path';\nimport { deriveRuntimeGraphReport, saveRuntimeGraph } from '../../lib/runtime-graph';\nimport { printAutoHiddenServers } from '../../lib/runtime-participant-resolver';\nimport { toError } from '../../toError';\n\nexport interface GenerateExecutorOptions {\n graphPath?: string;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\n/**\n * Generate the runtime microservice graph alongside the compile-time graph, DERIVED from the same\n * dependencies.json (its per-project apiRelations) — one regenerate produces both committed files, and\n * validate derives from the SAME source so they can't diverge. rpc APIs become direct runtime edges;\n * pubsub APIs become edges the viz draws through a queue.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction generateRuntimeGraph(\n workspaceRoot: string,\n graph: EnhancedGraph,\n hiddenProjects: Set<string>,\n apiContracts: ApiContracts,\n externalSystems: ExternalSystemDecls,\n): void {\n console.log('📡 Deriving runtime graph from dependencies.json (implements × uses per API)...');\n const report = deriveRuntimeGraphReport(graph, hiddenProjects, apiContracts, externalSystems);\n saveRuntimeGraph(report.graph, workspaceRoot);\n const serviceCount = Object.keys(report.graph.services).length;\n const queueCount = Object.keys(report.graph.queues).length;\n console.log(\n `✅ Runtime graph saved (${serviceCount} services, ${report.graph.runtimeEdges.length} runtime edges, ` +\n `${queueCount} queues, ${report.graph.triggers.length} cron/external triggers)`,\n );\n // Every edge the derivation had to GUESS at. Loud on purpose: a fanned-out edge is committed and\n // then reasoned about as if it were derived, which is how a fictional call survives for months.\n printRuntimeWarnings(report.warnings);\n printAutoHiddenServers(report.autoHidden);\n // Printed here, FAILED by validate-runtime-architecture: generate must still write the graph,\n // or the validator would have nothing to compare against and the error would be unfixable.\n if (report.problems.length > 0) {\n console.error(\n `❌ ${report.problems.length} client call(s) name a service no module answers to:`,\n );\n for (const problem of report.problems) console.error(` • ${problem}`);\n console.error(' This FAILS architecture:validate-runtime-architecture.');\n }\n}\n\n/** Print the derivation's guessed-edge warnings (nothing at all when the graph is fully targeted). */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction printRuntimeWarnings(warnings: string[]): void {\n if (warnings.length === 0) return;\n console.warn(`⚠️ ${warnings.length} runtime edge(s) could not be targeted to ONE service:`);\n for (const warning of warnings) console.warn(` • ${warning}`);\n}\n\n/**\n * Scan for api relations and attach them, surfacing any contract we could NOT map back to source.\n * Unresolved contracts mean the graph we are about to WRITE is incomplete, so they must be loud —\n * a green run that quietly omits a service gets committed, rendered, and trusted.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction scanApiRelations(\n workspaceRoot: string,\n graph: EnhancedGraph,\n projectInfos: Map<string, ProjectInfo>,\n graphPath: string = DEFAULT_GRAPH_PATH,\n): ScannedTables {\n console.log('📖 Assembling approved runtime-deps.json declarations (no source extraction)...');\n new ApprovedWiringGraph().attach(workspaceRoot, graph, projectInfos, graphPath);\n const saved = loadBlessedGraph(workspaceRoot, graphPath);\n if (saved === null)\n throw new RuleFailError(\n 'validate-runtime-architecture',\n 'Missing approved API contract references.',\n undefined,\n undefined,\n [\n new Option(\n 'Migrate src/wiring.ts and review runtime-deps.json and API contract approvals before generation.',\n true,\n ),\n ],\n );\n return new ScannedTables(\n new ApiContractFiles().load(workspaceRoot, graphPath, saved.apiContractFiles),\n saved.externalSystems,\n );\n}\n\n/** The two committed tables one scan produces, kept together so callers cannot persist just one. */\nclass ScannedTables {\n constructor(\n public readonly apiContracts: ApiContracts,\n public readonly externalSystems: ExternalSystemDecls,\n ) {}\n}\n\n/** Projects tagged drawOnGraph:false — kept in the JSON, omitted from every rendered graph. */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction hiddenProjectsIn(graph: EnhancedGraph): Set<string> {\n const hidden = new Set<string>();\n for (const name of Object.keys(graph)) {\n if (graph[name].drawOnGraph === false) hidden.add(name);\n }\n return hidden;\n}\n\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction printGraphSummary(graph: EnhancedGraph): void {\n const levels = new Set(Object.values(graph).map((entry: GraphEntry) => entry.level));\n console.log(`\\n📈 Graph Summary:`);\n console.log(` Projects: ${Object.keys(graph).length}`);\n console.log(` Levels: ${levels.size} (0-${Math.max(...levels)})`);\n}\n\n/**\n * The whole generation pipeline. Split out of runExecutor so that function stays a thin\n * try/catch-and-report shell — the error reporting is what a caller reads on failure, and it should\n * not be separated from the throw by fifty lines of steps.\n */\nexport class ArchitectureGenerator {\n constructor(\n private readonly visualizer: GraphVisualizer,\n private readonly impact: ImpactRefresh = new ImpactRefresh(),\n ) {}\n\n async generate(workspaceRoot: string, graphPath: string | undefined): Promise<void> {\n // Step 1: Build the full graph from nx, then transitively reduce it to the view\n console.log(\"📊 Generating dependency graph from nx's project graph...\");\n const reducedGraph = await generateReducedGraph();\n\n // Step 1b: The graph is a BUILD graph — refuse a cyclic one, naming EVERY cycle. This runs\n // before the sort deliberately: the sort also refuses, but reports one cycle and an\n // undifferentiated list of everything tangled with it, so a repo with several cycles pays one\n // full regeneration per cycle to discover them.\n console.log('🔄 Checking the project graph is acyclic...');\n const cycles = new ProjectCycleDetector();\n cycles.assertAcyclic(reducedGraph, 'the nx project graph');\n\n // Step 2: Topological sort (to assign levels for visualization)\n console.log('🔄 Computing topological layers...');\n const enhancedGraph = sortGraphTopologically(reducedGraph);\n // ...and assert the stratification it just produced actually holds: every dependency strictly\n // below its dependent. Safe to assert only because the graph was sorted a line ago — a stale\n // committed file is never checked this way.\n cycles.assertLevelsDescend(\n reducedGraph,\n cycles.levelsOf(enhancedGraph),\n 'the freshly sorted graph',\n );\n\n // Step 3: Enrich with AI metadata (framework, shortDescription, file\n // pointers). This VALIDATES (responsibilities.md required per project)\n // and throws before any write, so a failure never clobbers the file.\n console.log('🏷️ Enriching graph with framework + responsibilities metadata...');\n const projectInfos = await collectProjectInfo();\n enrichGraph(enhancedGraph, projectInfos, workspaceRoot);\n // enrichGraph pinned servers, clients and apps to the top row (#1179): prove the promotion\n // kept every dependency strictly below its dependent before anything is written.\n cycles.assertLevelsDescend(reducedGraph, cycles.levelsOf(enhancedGraph), 'the entry-point-promoted graph');\n new TagTruthCheck().assertTrue(enhancedGraph, projectInfos, workspaceRoot); // #1064, before any write\n\n // Step 3b: Classify each api-lib edge (implements/uses + rpc/pubsub) by\n // scanning source, so dependencies.json + the viz + the runtime graph all\n // read the same derived truth.\n const scanned = scanApiRelations(workspaceRoot, enhancedGraph, projectInfos, graphPath);\n const apiContracts = scanned.apiContracts;\n\n saveApprovedGraph(workspaceRoot, graphPath ?? DEFAULT_GRAPH_PATH, enhancedGraph, scanned);\n\n // Step 4b: Write the committed, clickable HTML view next to the JSON so\n // dependencies.html regenerates in lock-step with dependencies.json.\n const vizPaths = this.visualizer.writeVisualization(enhancedGraph, workspaceRoot);\n console.log(`✅ Wrote ${vizPaths.htmlPath}`);\n\n // Step 4c: The page's Impact mode — nx affected for \"Changed on this branch\" ONLY, into a\n // gitignored sidecar beside the html (never into the committed files). Generate never scans\n // the last commit; that comparison is arch:visualize's. Optional: it reports, it never throws.\n console.log(\n await this.impact.run(path.dirname(vizPaths.htmlPath), workspaceRoot, enhancedGraph, GENERATE_IMPACT_KINDS),\n );\n\n // Step 5: Generate the runtime microservice graph from the same scan.\n // Projects tagged drawOnGraph:false are threaded through so the runtime\n // graph hides them too (they stay flagged in runtime-dependencies.json).\n generateRuntimeGraph(\n workspaceRoot,\n enhancedGraph,\n hiddenProjectsIn(enhancedGraph),\n apiContracts,\n scanned.externalSystems,\n );\n\n printGraphSummary(enhancedGraph);\n }\n}\n\n// webpieces-disable no-function-outside-class -- persist the approved graph and contract tables together\nfunction saveApprovedGraph(\n workspaceRoot: string,\n effectiveGraphPath: string,\n enhancedGraph: EnhancedGraph,\n scanned: ScannedTables,\n): void {\n // Step 4: Write one contract file per API (architecture/apis/<Api>.json) and delete the files of\n // APIs that no longer exist. dependencies.json only LINKS to them, so an endpoint change rewrites\n // an api file and never the dependency graph. The runtime validator reads these files back.\n const apiContracts = scanned.apiContracts;\n const contractFiles = new ApiContractFiles();\n const written = contractFiles.write(workspaceRoot, effectiveGraphPath, apiContracts);\n console.log(`✅ Wrote ${written.written.length} api contract file(s)`);\n for (const deleted of written.deleted)\n console.log(`🗑️ Deleted stale api contract file ${deleted}`);\n\n // Step 4a: Save the graph, INCLUDING the contract-file links and the external-system\n // declarations the runtime derivation reads back — generate derives from the in-memory graph,\n // validate from the files, so anything not written here would make the two disagree.\n console.log('💾 Saving graph to architecture/dependencies.json...');\n const contractRefs = contractFiles.refsFor(apiContracts);\n saveGraph(\n enhancedGraph,\n workspaceRoot,\n effectiveGraphPath,\n contractRefs,\n scanned.externalSystems,\n );\n console.log('✅ Graph saved successfully');\n}\n\nexport default async function runExecutor(\n options: GenerateExecutorOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n const graphPath = options.graphPath;\n const workspaceRoot = context.root;\n\n console.log('\\n📊 Architecture Graph Generator\\n');\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n await new ArchitectureGenerator(new GraphVisualizer()).generate(workspaceRoot, graphPath);\n return { success: true };\n } catch (err: unknown) {\n const error = toError(err);\n // A RuleFailError carries its cures as Option[]; `Error.message` is only the aiMessage, so\n // printing that alone silently drops them. renderRuleFailForHuman is the ONE renderer that\n // numbers them, and this catch is the top-level handler for the nx target.\n const rendered =\n error instanceof RuleFailError ? renderRuleFailForHuman(error) : error.message;\n console.error('❌ Graph generation failed:', rendered);\n if (error instanceof MetadataValidationError) {\n const mdPath = writeTemplate(workspaceRoot, 'webpieces.responsibilities.md');\n console.error('');\n console.error(\n '⚠️ *** Refer to ' +\n mdPath +\n ' for how to author responsibilities.md files *** ⚠️',\n );\n }\n return { success: false };\n }\n}\n"]}
package/src/index.d.ts CHANGED
@@ -25,6 +25,9 @@ export { WiringFormatConfig } from './configs/wiring-format-config';
25
25
  export { NoRootUnionApiTypeConfig } from './configs/no-root-union-config';
26
26
  export { API_DOC_MODES, ApiRulesForOpenApiConfig, ApiRulesForMcpConfig } from './configs/api-doc-rules-config';
27
27
  export type { ApiDocMode } from './configs/api-doc-rules-config';
28
- export { ApiClientPackagesEntry, ApiLibDependenciesConfig, ApiLibPathConfig, FrameworkFolderEntry, FrameworkFolderConfig } from './configs/tag-truth-configs';
29
- export { GraphRenderModel, RenderNode, RenderEdge } from './lib/graph-render-model';
30
- export { GraphMode, NodeModeDots, LegendFacts } from './lib/graph-color-modes';
28
+ export { ApiClientPackagesEntry, ApiLibDependenciesConfig, ApiLibPathConfig, FrameworkFolderEntry, FrameworkFolderConfig, ProductTagsConfig } from './configs/tag-truth-configs';
29
+ export { GraphRenderModel, RenderNode, RenderEdge, NodeTags } from './lib/graph-render-model';
30
+ export { GraphMode, NodeModeDots, LegendFacts, ProductCount } from './lib/graph-color-modes';
31
+ export { ProductMembership, RuntimeProductMembership, ProductPalette, ProductColor } from './lib/graph-products';
32
+ export { ProductResolver, ProductResolution, PRODUCT_TAG_PREFIX } from './lib/product-resolver';
33
+ export { EntryPointLevels } from './lib/graph-entry-levels';
package/src/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LegendFacts = exports.NodeModeDots = exports.GraphMode = exports.RenderEdge = exports.RenderNode = exports.GraphRenderModel = exports.FrameworkFolderConfig = exports.FrameworkFolderEntry = exports.ApiLibPathConfig = exports.ApiLibDependenciesConfig = exports.ApiClientPackagesEntry = exports.ApiRulesForMcpConfig = exports.ApiRulesForOpenApiConfig = exports.API_DOC_MODES = exports.NoRootUnionApiTypeConfig = exports.WiringFormatConfig = exports.ValidateEslintSyncConfig = exports.ValidateVersionsLockedConfig = exports.ValidatePackageJsonConfig = exports.ValidateNoArchitectureCyclesConfig = exports.ValidateArchitectureUnchangedConfig = exports.MissingDesignAnnotationConfig = exports.DiGraphConfig = exports.NxWiringConfig = exports.RuntimeArchitectureConfig = exports.NoFileImportCyclesConfig = exports.STRUCTURAL_MODES = exports.createNodesV2 = void 0;
3
+ exports.EntryPointLevels = exports.PRODUCT_TAG_PREFIX = exports.ProductResolution = exports.ProductResolver = exports.ProductColor = exports.ProductPalette = exports.RuntimeProductMembership = exports.ProductMembership = exports.ProductCount = exports.LegendFacts = exports.NodeModeDots = exports.GraphMode = exports.NodeTags = exports.RenderEdge = exports.RenderNode = exports.GraphRenderModel = exports.ProductTagsConfig = exports.FrameworkFolderConfig = exports.FrameworkFolderEntry = exports.ApiLibPathConfig = exports.ApiLibDependenciesConfig = exports.ApiClientPackagesEntry = exports.ApiRulesForMcpConfig = exports.ApiRulesForOpenApiConfig = exports.API_DOC_MODES = exports.NoRootUnionApiTypeConfig = exports.WiringFormatConfig = exports.ValidateEslintSyncConfig = exports.ValidateVersionsLockedConfig = exports.ValidatePackageJsonConfig = exports.ValidateNoArchitectureCyclesConfig = exports.ValidateArchitectureUnchangedConfig = exports.MissingDesignAnnotationConfig = exports.DiGraphConfig = exports.NxWiringConfig = exports.RuntimeArchitectureConfig = exports.NoFileImportCyclesConfig = exports.STRUCTURAL_MODES = exports.createNodesV2 = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  tslib_1.__exportStar(require("./lib/graph-generator"), exports);
6
6
  tslib_1.__exportStar(require("./lib/graph-sorter"), exports);
@@ -45,12 +45,26 @@ Object.defineProperty(exports, "ApiLibDependenciesConfig", { enumerable: true, g
45
45
  Object.defineProperty(exports, "ApiLibPathConfig", { enumerable: true, get: function () { return tag_truth_configs_1.ApiLibPathConfig; } });
46
46
  Object.defineProperty(exports, "FrameworkFolderEntry", { enumerable: true, get: function () { return tag_truth_configs_1.FrameworkFolderEntry; } });
47
47
  Object.defineProperty(exports, "FrameworkFolderConfig", { enumerable: true, get: function () { return tag_truth_configs_1.FrameworkFolderConfig; } });
48
+ Object.defineProperty(exports, "ProductTagsConfig", { enumerable: true, get: function () { return tag_truth_configs_1.ProductTagsConfig; } });
48
49
  var graph_render_model_1 = require("./lib/graph-render-model");
49
50
  Object.defineProperty(exports, "GraphRenderModel", { enumerable: true, get: function () { return graph_render_model_1.GraphRenderModel; } });
50
51
  Object.defineProperty(exports, "RenderNode", { enumerable: true, get: function () { return graph_render_model_1.RenderNode; } });
51
52
  Object.defineProperty(exports, "RenderEdge", { enumerable: true, get: function () { return graph_render_model_1.RenderEdge; } });
53
+ Object.defineProperty(exports, "NodeTags", { enumerable: true, get: function () { return graph_render_model_1.NodeTags; } });
52
54
  var graph_color_modes_1 = require("./lib/graph-color-modes");
53
55
  Object.defineProperty(exports, "GraphMode", { enumerable: true, get: function () { return graph_color_modes_1.GraphMode; } });
54
56
  Object.defineProperty(exports, "NodeModeDots", { enumerable: true, get: function () { return graph_color_modes_1.NodeModeDots; } });
55
57
  Object.defineProperty(exports, "LegendFacts", { enumerable: true, get: function () { return graph_color_modes_1.LegendFacts; } });
58
+ Object.defineProperty(exports, "ProductCount", { enumerable: true, get: function () { return graph_color_modes_1.ProductCount; } });
59
+ var graph_products_1 = require("./lib/graph-products");
60
+ Object.defineProperty(exports, "ProductMembership", { enumerable: true, get: function () { return graph_products_1.ProductMembership; } });
61
+ Object.defineProperty(exports, "RuntimeProductMembership", { enumerable: true, get: function () { return graph_products_1.RuntimeProductMembership; } });
62
+ Object.defineProperty(exports, "ProductPalette", { enumerable: true, get: function () { return graph_products_1.ProductPalette; } });
63
+ Object.defineProperty(exports, "ProductColor", { enumerable: true, get: function () { return graph_products_1.ProductColor; } });
64
+ var product_resolver_1 = require("./lib/product-resolver");
65
+ Object.defineProperty(exports, "ProductResolver", { enumerable: true, get: function () { return product_resolver_1.ProductResolver; } });
66
+ Object.defineProperty(exports, "ProductResolution", { enumerable: true, get: function () { return product_resolver_1.ProductResolution; } });
67
+ Object.defineProperty(exports, "PRODUCT_TAG_PREFIX", { enumerable: true, get: function () { return product_resolver_1.PRODUCT_TAG_PREFIX; } });
68
+ var graph_entry_levels_1 = require("./lib/graph-entry-levels");
69
+ Object.defineProperty(exports, "EntryPointLevels", { enumerable: true, get: function () { return graph_entry_levels_1.EntryPointLevels; } });
56
70
  //# sourceMappingURL=index.js.map
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/nx-webpieces-rules/src/index.ts"],"names":[],"mappings":";;;;AAAA,gEAAsC;AACtC,6DAAmC;AACnC,iEAAuC;AACvC,kEAAwC;AACxC,6DAAmC;AACnC,iEAAuC;AACvC,+DAAqC;AACrC,mEAAyC;AACzC,6EAAmD;AACnD,6DAAmC;AACnC,iEAAuC;AACvC,6DAAmC;AACnC,2EAAiD;AACjD,uEAA6C;AAC7C,qCAAyC;AAChC,8FADA,sBAAa,OACA;AACtB,kBAAe,EAAE,IAAI,EAAE,+BAA+B,EAAE,aAAa,EAAb,sBAAa,EAAE,CAAC;AAExE,uDAAyU;AAAhU,gHAAA,gBAAgB,OAAA;AAAE,wHAAA,wBAAwB,OAAA;AAAE,yHAAA,yBAAyB,OAAA;AAAE,8GAAA,cAAc,OAAA;AAAE,6GAAA,aAAa,OAAA;AAAE,6HAAA,6BAA6B,OAAA;AAAE,mIAAA,mCAAmC,OAAA;AAAE,kIAAA,kCAAkC,OAAA;AAAE,yHAAA,yBAAyB,OAAA;AAAE,4HAAA,4BAA4B,OAAA;AAAE,wHAAA,wBAAwB,OAAA;AAExS,uEAAoE;AAA3D,0HAAA,kBAAkB,OAAA;AAC3B,uEAA0E;AAAjE,gIAAA,wBAAwB,OAAA;AACjC,uEAA+G;AAAtG,qHAAA,aAAa,OAAA;AAAE,gIAAA,wBAAwB,OAAA;AAAE,4HAAA,oBAAoB,OAAA;AAEtE,iEAA8J;AAArJ,2HAAA,sBAAsB,OAAA;AAAE,6HAAA,wBAAwB,OAAA;AAAE,qHAAA,gBAAgB,OAAA;AAAE,yHAAA,oBAAoB,OAAA;AAAE,0HAAA,qBAAqB,OAAA;AAExH,+DAAoF;AAA3E,sHAAA,gBAAgB,OAAA;AAAE,gHAAA,UAAU,OAAA;AAAE,gHAAA,UAAU,OAAA;AACjD,6DAA+E;AAAtE,8GAAA,SAAS,OAAA;AAAE,iHAAA,YAAY,OAAA;AAAE,gHAAA,WAAW,OAAA","sourcesContent":["export * from './lib/graph-generator';\nexport * from './lib/graph-sorter';\nexport * from './lib/graph-comparator';\nexport * from './lib/package-validator';\nexport * from './lib/graph-loader';\nexport * from './lib/graph-visualizer';\nexport * from './lib/graph-metadata';\nexport * from './lib/framework-resolver';\nexport * from './lib/runtime-participant-resolver';\nexport * from './lib/project-info';\nexport * from './lib/responsibilities';\nexport * from './lib/di-graph/dot';\nexport * from './lib/di-graph/design-visualizer';\nexport * from './lib/di-graph/design-finder';\nimport { createNodesV2 } from './plugin';\nexport { createNodesV2 };\nexport default { name: '@webpieces/nx-webpieces-rules', createNodesV2 };\n\nexport { STRUCTURAL_MODES, NoFileImportCyclesConfig, RuntimeArchitectureConfig, NxWiringConfig, DiGraphConfig, MissingDesignAnnotationConfig, ValidateArchitectureUnchangedConfig, ValidateNoArchitectureCyclesConfig, ValidatePackageJsonConfig, ValidateVersionsLockedConfig, ValidateEslintSyncConfig } from './configs/rule-configs';\nexport type { StructuralMode } from './configs/rule-configs';\nexport { WiringFormatConfig } from './configs/wiring-format-config';\nexport { NoRootUnionApiTypeConfig } from './configs/no-root-union-config';\nexport { API_DOC_MODES, ApiRulesForOpenApiConfig, ApiRulesForMcpConfig } from './configs/api-doc-rules-config';\nexport type { ApiDocMode } from './configs/api-doc-rules-config';\nexport { ApiClientPackagesEntry, ApiLibDependenciesConfig, ApiLibPathConfig, FrameworkFolderEntry, FrameworkFolderConfig } from './configs/tag-truth-configs';\n\nexport { GraphRenderModel, RenderNode, RenderEdge } from './lib/graph-render-model';\nexport { GraphMode, NodeModeDots, LegendFacts } from './lib/graph-color-modes';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/nx-webpieces-rules/src/index.ts"],"names":[],"mappings":";;;;AAAA,gEAAsC;AACtC,6DAAmC;AACnC,iEAAuC;AACvC,kEAAwC;AACxC,6DAAmC;AACnC,iEAAuC;AACvC,+DAAqC;AACrC,mEAAyC;AACzC,6EAAmD;AACnD,6DAAmC;AACnC,iEAAuC;AACvC,6DAAmC;AACnC,2EAAiD;AACjD,uEAA6C;AAC7C,qCAAyC;AAChC,8FADA,sBAAa,OACA;AACtB,kBAAe,EAAE,IAAI,EAAE,+BAA+B,EAAE,aAAa,EAAb,sBAAa,EAAE,CAAC;AAExE,uDAAyU;AAAhU,gHAAA,gBAAgB,OAAA;AAAE,wHAAA,wBAAwB,OAAA;AAAE,yHAAA,yBAAyB,OAAA;AAAE,8GAAA,cAAc,OAAA;AAAE,6GAAA,aAAa,OAAA;AAAE,6HAAA,6BAA6B,OAAA;AAAE,mIAAA,mCAAmC,OAAA;AAAE,kIAAA,kCAAkC,OAAA;AAAE,yHAAA,yBAAyB,OAAA;AAAE,4HAAA,4BAA4B,OAAA;AAAE,wHAAA,wBAAwB,OAAA;AAExS,uEAAoE;AAA3D,0HAAA,kBAAkB,OAAA;AAC3B,uEAA0E;AAAjE,gIAAA,wBAAwB,OAAA;AACjC,uEAA+G;AAAtG,qHAAA,aAAa,OAAA;AAAE,gIAAA,wBAAwB,OAAA;AAAE,4HAAA,oBAAoB,OAAA;AAEtE,iEAAiL;AAAxK,2HAAA,sBAAsB,OAAA;AAAE,6HAAA,wBAAwB,OAAA;AAAE,qHAAA,gBAAgB,OAAA;AAAE,yHAAA,oBAAoB,OAAA;AAAE,0HAAA,qBAAqB,OAAA;AAAE,sHAAA,iBAAiB,OAAA;AAE3I,+DAA8F;AAArF,sHAAA,gBAAgB,OAAA;AAAE,gHAAA,UAAU,OAAA;AAAE,gHAAA,UAAU,OAAA;AAAE,8GAAA,QAAQ,OAAA;AAC3D,6DAA6F;AAApF,8GAAA,SAAS,OAAA;AAAE,iHAAA,YAAY,OAAA;AAAE,gHAAA,WAAW,OAAA;AAAE,iHAAA,YAAY,OAAA;AAC3D,uDAAiH;AAAxG,mHAAA,iBAAiB,OAAA;AAAE,0HAAA,wBAAwB,OAAA;AAAE,gHAAA,cAAc,OAAA;AAAE,8GAAA,YAAY,OAAA;AAClF,2DAAgG;AAAvF,mHAAA,eAAe,OAAA;AAAE,qHAAA,iBAAiB,OAAA;AAAE,sHAAA,kBAAkB,OAAA;AAC/D,+DAA4D;AAAnD,sHAAA,gBAAgB,OAAA","sourcesContent":["export * from './lib/graph-generator';\nexport * from './lib/graph-sorter';\nexport * from './lib/graph-comparator';\nexport * from './lib/package-validator';\nexport * from './lib/graph-loader';\nexport * from './lib/graph-visualizer';\nexport * from './lib/graph-metadata';\nexport * from './lib/framework-resolver';\nexport * from './lib/runtime-participant-resolver';\nexport * from './lib/project-info';\nexport * from './lib/responsibilities';\nexport * from './lib/di-graph/dot';\nexport * from './lib/di-graph/design-visualizer';\nexport * from './lib/di-graph/design-finder';\nimport { createNodesV2 } from './plugin';\nexport { createNodesV2 };\nexport default { name: '@webpieces/nx-webpieces-rules', createNodesV2 };\n\nexport { STRUCTURAL_MODES, NoFileImportCyclesConfig, RuntimeArchitectureConfig, NxWiringConfig, DiGraphConfig, MissingDesignAnnotationConfig, ValidateArchitectureUnchangedConfig, ValidateNoArchitectureCyclesConfig, ValidatePackageJsonConfig, ValidateVersionsLockedConfig, ValidateEslintSyncConfig } from './configs/rule-configs';\nexport type { StructuralMode } from './configs/rule-configs';\nexport { WiringFormatConfig } from './configs/wiring-format-config';\nexport { NoRootUnionApiTypeConfig } from './configs/no-root-union-config';\nexport { API_DOC_MODES, ApiRulesForOpenApiConfig, ApiRulesForMcpConfig } from './configs/api-doc-rules-config';\nexport type { ApiDocMode } from './configs/api-doc-rules-config';\nexport { ApiClientPackagesEntry, ApiLibDependenciesConfig, ApiLibPathConfig, FrameworkFolderEntry, FrameworkFolderConfig, ProductTagsConfig } from './configs/tag-truth-configs';\n\nexport { GraphRenderModel, RenderNode, RenderEdge, NodeTags } from './lib/graph-render-model';\nexport { GraphMode, NodeModeDots, LegendFacts, ProductCount } from './lib/graph-color-modes';\nexport { ProductMembership, RuntimeProductMembership, ProductPalette, ProductColor } from './lib/graph-products';\nexport { ProductResolver, ProductResolution, PRODUCT_TAG_PREFIX } from './lib/product-resolver';\nexport { EntryPointLevels } from './lib/graph-entry-levels';\n"]}
@@ -20,12 +20,22 @@
20
20
  * from cache, unchanged); everything else is grey. Which box is which is only known
21
21
  * per checkout, so every box carries all four variants and the page picks one from the
22
22
  * sidecar (graph-impact.ts). The state is never written on the box: color + legend.
23
+ * The words are the Filter's: changed · dependent · dependency (built, unchanged) ·
24
+ * not in this build.
25
+ * - PRODUCT "which products it belongs to" (#1179, graph-products.ts). One product is a solid
26
+ * fill in that product's palette color; two or more (but not all) are the same
27
+ * vertical stripes the multi-runtime box uses, one per product; a box shared by EVERY
28
+ * product is one neutral fill (otherwise a shared foundation would be four-stripe
29
+ * noise); a box in no product is white with a dashed border. The Filter HIDES other
30
+ * products' boxes; this mode keeps every box and makes ownership visible.
23
31
  */
24
32
  import { KnownFramework } from '@webpieces/rules-sdk';
33
+ import { ProductPalette } from './graph-products';
25
34
  export declare enum GraphMode {
26
35
  RUNTIME = "runtime",
27
36
  ARCHITECTURE = "architecture",
28
- IMPACT = "impact"
37
+ IMPACT = "impact",
38
+ PRODUCT = "product"
29
39
  }
30
40
  /** One selectable mode as the drawer's Color-by pulldown shows it. */
31
41
  export declare class ModeInfo {
@@ -34,7 +44,7 @@ export declare class ModeInfo {
34
44
  readonly subtitle: string;
35
45
  constructor(mode: GraphMode, name: string, subtitle: string);
36
46
  }
37
- /** In key order: `1` selects the first, `2` the second, `3` the third. */
47
+ /** In key order: `1` selects the first, `2` the second, `3` the third, `4` the fourth. */
38
48
  export declare const GRAPH_MODES: readonly ModeInfo[];
39
49
  /** Text on a light fill. */
40
50
  export declare const DARK_TEXT = "#1a1c22";
@@ -96,7 +106,11 @@ export declare class NodeFacts {
96
106
  readonly level: number;
97
107
  readonly role: string;
98
108
  readonly frameworks: string[];
99
- constructor(nodeId: string, shortName: string, level: number, role: string, frameworks: string[]);
109
+ /** The products the project belongs to (dependencies.json `products`), sorted; empty for none. */
110
+ readonly products: string[];
111
+ constructor(nodeId: string, shortName: string, level: number, role: string, frameworks: string[],
112
+ /** The products the project belongs to (dependencies.json `products`), sorted; empty for none. */
113
+ products: string[]);
100
114
  }
101
115
  /** One box's DOT statement for every mode (and for each impact state). */
102
116
  export declare class NodeModeDots {
@@ -106,7 +120,15 @@ export declare class NodeModeDots {
106
120
  readonly affected: string;
107
121
  readonly buildInput: string;
108
122
  readonly untouched: string;
109
- constructor(runtime: string, architecture: string, touched: string, affected: string, buildInput: string, untouched: string);
123
+ readonly product: string;
124
+ constructor(runtime: string, architecture: string, touched: string, affected: string, buildInput: string, untouched: string, product: string);
125
+ }
126
+ /** How many drawn boxes one product holds, and how many of them no other product does. */
127
+ export declare class ProductCount {
128
+ readonly product: string;
129
+ total: number;
130
+ only: number;
131
+ constructor(product: string, total: number, only: number);
110
132
  }
111
133
  /** Which colors and roles the drawn projects actually use, so the legend lists exactly those. */
112
134
  export declare class LegendFacts {
@@ -114,6 +136,16 @@ export declare class LegendFacts {
114
136
  roles: string[];
115
137
  multiRuntime: boolean;
116
138
  noRuntime: boolean;
139
+ /** One count per declared product, in palette (sorted-name) order; empty when none is declared. */
140
+ products: ProductCount[];
141
+ /** Boxes shared by every product (two or more declared). */
142
+ sharedByAll: number;
143
+ /** Boxes in no product. */
144
+ noProduct: number;
145
+ /** Some box belongs to two or more products, but not to every one: it is striped. */
146
+ multiProduct: boolean;
147
+ /** Some product is past the palette's eight and is painted with the neutral fill. */
148
+ productOverflow: boolean;
117
149
  }
118
150
  export declare class NodeModeStyler {
119
151
  styleOf(framework: string): FrameworkStyle | undefined;
@@ -124,9 +156,11 @@ export declare class NodeModeStyler {
124
156
  bases(frameworks: string[]): KnownFramework[];
125
157
  /** The specialization drawn inside `base`, if the set carries one. */
126
158
  specialization(base: KnownFramework, frameworks: string[]): FrameworkStyle | undefined;
127
- dots(facts: NodeFacts): NodeModeDots;
159
+ dots(facts: NodeFacts, palette: ProductPalette): NodeModeDots;
128
160
  /** Fold one drawn box into the legend facts. */
129
- record(legend: LegendFacts, facts: NodeFacts): void;
161
+ record(legend: LegendFacts, facts: NodeFacts, palette: ProductPalette): void;
162
+ /** The Product section's counts: per product (total, and only-this-product), shared-by-all, none. */
163
+ private recordProducts;
130
164
  /**
131
165
  * Line 3 of every box: every framework tag, bases before specializations, any tag outside the
132
166
  * shared vocabulary last (it still runs somewhere somebody declared).
@@ -140,9 +174,19 @@ export declare class NodeModeStyler {
140
174
  labelText(facts: NodeFacts, fontColor: string): string;
141
175
  /** `filled` for one runtime; equal `striped` weights for several (the last takes the remainder). */
142
176
  stripeAttrs(bases: KnownFramework[]): string;
177
+ /**
178
+ * One equal vertical stripe per color — the renderer the multi-runtime box and the multi-product
179
+ * box share. A single color is a plain fill.
180
+ */
181
+ stripes(colors: string[]): string;
143
182
  private statement;
144
183
  /** A solid fill, the three lines on it. `extra` carries border attributes. */
145
184
  private plain;
185
+ /**
186
+ * Product mode (#1179): one product solid in its color; several (not all) striped, one stripe per
187
+ * product; every product the neutral shared fill; no product a white box with a dashed border.
188
+ */
189
+ private product;
146
190
  /**
147
191
  * Runtime mode. No runtime, or one runtime with no specialization: a solid fill. One runtime with a
148
192
  * specialization: NESTED. Several runtimes: full-height stripes with the text across them (a