@webpieces/nx-webpieces-rules 0.4.898 → 0.4.900
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/README.md +5 -1
- package/package.json +7 -7
- package/src/configs/tag-truth-configs.d.ts +31 -5
- package/src/configs/tag-truth-configs.js +37 -6
- package/src/configs/tag-truth-configs.js.map +1 -1
- package/src/executors/generate/executor.js +3 -3
- package/src/executors/generate/executor.js.map +1 -1
- package/src/executors/visualize/executor.js +3 -1
- package/src/executors/visualize/executor.js.map +1 -1
- package/src/index.d.ts +3 -2
- package/src/index.js +5 -1
- package/src/index.js.map +1 -1
- package/src/lib/graph-color-modes.d.ts +8 -0
- package/src/lib/graph-color-modes.js +7 -0
- package/src/lib/graph-color-modes.js.map +1 -1
- package/src/lib/graph-filter.client.d.ts +5 -2
- package/src/lib/graph-filter.client.js +9 -4
- package/src/lib/graph-filter.client.js.map +1 -1
- package/src/lib/graph-generator.d.ts +3 -1
- package/src/lib/graph-generator.js +22 -3
- package/src/lib/graph-generator.js.map +1 -1
- package/src/lib/graph-legend.d.ts +8 -0
- package/src/lib/graph-legend.js +24 -2
- package/src/lib/graph-legend.js.map +1 -1
- package/src/lib/graph-products.d.ts +14 -0
- package/src/lib/graph-products.js +22 -1
- package/src/lib/graph-products.js.map +1 -1
- package/src/lib/graph-render-model.d.ts +19 -2
- package/src/lib/graph-render-model.js +19 -4
- package/src/lib/graph-render-model.js.map +1 -1
- package/src/lib/graph-visualizer.client.d.ts +2 -0
- package/src/lib/graph-visualizer.client.js +6 -0
- package/src/lib/graph-visualizer.client.js.map +1 -1
- package/src/lib/graph-visualizer.d.ts +11 -1
- package/src/lib/graph-visualizer.js +26 -7
- package/src/lib/graph-visualizer.js.map +1 -1
- package/src/lib/product-dependencies.d.ts +52 -0
- package/src/lib/product-dependencies.js +128 -0
- package/src/lib/product-dependencies.js.map +1 -0
- package/src/lib/tag-truth.d.ts +12 -3
- package/src/lib/tag-truth.js +27 -4
- package/src/lib/tag-truth.js.map +1 -1
- package/src/lib/visible-transitive-reduction.d.ts +33 -0
- package/src/lib/visible-transitive-reduction.js +60 -0
- package/src/lib/visible-transitive-reduction.js.map +1 -0
- package/src/rule-help.js +1 -1
- package/src/rule-help.js.map +1 -1
- package/src/rule-settings.js +1 -0
- package/src/rule-settings.js.map +1 -1
package/README.md
CHANGED
|
@@ -166,7 +166,11 @@ is that promoted level. The runtime graph keeps its call-depth levels.
|
|
|
166
166
|
sorted-name order; any product past 8 uses the neutral fill and the legend says so); two or more,
|
|
167
167
|
but not all, are vertical stripes, one per product; a box shared by every product is one neutral
|
|
168
168
|
fill; a box in no product is white with a dashed border. The legend counts each product's boxes
|
|
169
|
-
and how many belong to it alone.
|
|
169
|
+
and how many belong to it alone. A dependency that crosses into another product (its target does
|
|
170
|
+
not carry every product of its source) is drawn dashed in the color of the product it depends on, so
|
|
171
|
+
product boundaries read at a glance while same-product edges stay plain (#1204); the legend counts
|
|
172
|
+
those edges and lists the `product-tags` rule's allowed matrix (`example → openapi, platform`).
|
|
173
|
+
Which crossings are ALLOWED is `product-tags.productDependencies`: generation refuses any other.
|
|
170
174
|
|
|
171
175
|
**Filter** opens a popover of four groups that INTERSECT: the changes of the active Impact
|
|
172
176
|
comparison, headed by its label and carrying the same branch / last-commit toggle (All ·
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/nx-webpieces-rules",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.900",
|
|
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.
|
|
24
|
-
"@webpieces/core-util": "0.4.
|
|
25
|
-
"@webpieces/eslint-rules": "0.4.
|
|
26
|
-
"@webpieces/rules-config": "0.4.
|
|
23
|
+
"@webpieces/api-doc-model": "0.4.900",
|
|
24
|
+
"@webpieces/core-util": "0.4.900",
|
|
25
|
+
"@webpieces/eslint-rules": "0.4.900",
|
|
26
|
+
"@webpieces/rules-config": "0.4.900",
|
|
27
27
|
"madge": "8.0.0",
|
|
28
|
-
"@webpieces/rules-sdk": "0.4.
|
|
28
|
+
"@webpieces/rules-sdk": "0.4.900"
|
|
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.
|
|
44
|
+
"@webpieces/tooling-testkit": "0.4.900"
|
|
45
45
|
},
|
|
46
46
|
"types": "./src/index.d.ts",
|
|
47
47
|
"bin": {
|
|
@@ -71,17 +71,43 @@ export declare class FrameworkFolderConfig extends BaseRuleConfig {
|
|
|
71
71
|
static readonly SCHEMA: SchemaShape<FrameworkFolderConfig>;
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
* One row of the product dependency matrix (#1204): the OTHER products a product may depend on.
|
|
75
|
+
* Data-only.
|
|
76
|
+
*/
|
|
77
|
+
export declare class ProductDependencyEntry {
|
|
78
|
+
/** The product this row is about — the name after `product:` in some project's tags, e.g. `example`. */
|
|
79
|
+
name: string;
|
|
80
|
+
/**
|
|
81
|
+
* The other products a project of `name` may depend on, e.g. `["platform", "openapi"]`. A product
|
|
82
|
+
* may always depend on itself, so it is never listed; `[]` states that it depends on no other.
|
|
83
|
+
*/
|
|
84
|
+
allowedToDependOn: string[];
|
|
85
|
+
static readonly SCHEMA: SchemaShape<ProductDependencyEntry>;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* `product-tags` (#1179, #1204) — two halves behind ONE switch:
|
|
89
|
+
* - every project whose role is in `roles` carries at least one well-formed `product:<name>` nx tag
|
|
90
|
+
* (lowercase kebab, several allowed). The product filter and the Product color mode of
|
|
91
|
+
* `arch:visualize` / `arch:visualize-runtime` seed a product's membership from exactly those tags,
|
|
92
|
+
* so an untagged server, client or app silently drops out of every product view;
|
|
93
|
+
* - every edge of the DEPENDENCY graph respects `productDependencies`: an edge A -> B is legal when,
|
|
94
|
+
* for EVERY product p of A, B carries p or a product p's entry allows. Membership is the resolved
|
|
95
|
+
* one (a project's own tags are final; an untagged project inherits). The runtime graph is not
|
|
96
|
+
* checked.
|
|
78
97
|
*
|
|
79
|
-
* - `mode` — OFF | RUN_EVERY_TIME (a graph rule: it reads every project, every run).
|
|
98
|
+
* - `mode` — OFF | RUN_EVERY_TIME (a graph rule: it reads every project, every run). OFF turns off
|
|
99
|
+
* both halves.
|
|
80
100
|
* - `roles` — required, non-empty: WHICH roles are refused without a tag, e.g.
|
|
81
101
|
* `["server", "client", "app"]`. It decides which projects fail, so it is behaviour, never a default.
|
|
102
|
+
* - `productDependencies` — required; `[]` is valid and means no product may depend on another. One
|
|
103
|
+
* entry per product that DOES depend on others, e.g.
|
|
104
|
+
* `[{ "name": "example", "allowedToDependOn": ["platform", "openapi"] }]`. A product with no entry
|
|
105
|
+
* may depend only on itself. A `name` or `allowedToDependOn` value that no `product:` tag uses, and a
|
|
106
|
+
* duplicate `name`, fail generation naming it — a typo can never silently allow or forbid nothing.
|
|
82
107
|
*/
|
|
83
108
|
export declare class ProductTagsConfig extends BaseRuleConfig {
|
|
84
109
|
mode?: StructuralMode;
|
|
85
110
|
roles: string[];
|
|
111
|
+
productDependencies: ProductDependencyEntry[];
|
|
86
112
|
static readonly SCHEMA: SchemaShape<ProductTagsConfig>;
|
|
87
113
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ProductTagsConfig = exports.FrameworkFolderConfig = exports.FrameworkFolderEntry = exports.ApiLibPathConfig = exports.ApiLibDependenciesConfig = exports.ApiClientPackagesEntry = void 0;
|
|
3
|
+
exports.ProductTagsConfig = exports.ProductDependencyEntry = 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. */
|
|
@@ -96,20 +96,51 @@ class FrameworkFolderConfig extends rules_sdk_1.BaseRuleConfig {
|
|
|
96
96
|
}
|
|
97
97
|
exports.FrameworkFolderConfig = FrameworkFolderConfig;
|
|
98
98
|
/**
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
* One row of the product dependency matrix (#1204): the OTHER products a product may depend on.
|
|
100
|
+
* Data-only.
|
|
101
|
+
*/
|
|
102
|
+
class ProductDependencyEntry {
|
|
103
|
+
/** The product this row is about — the name after `product:` in some project's tags, e.g. `example`. */
|
|
104
|
+
name;
|
|
105
|
+
/**
|
|
106
|
+
* The other products a project of `name` may depend on, e.g. `["platform", "openapi"]`. A product
|
|
107
|
+
* may always depend on itself, so it is never listed; `[]` states that it depends on no other.
|
|
108
|
+
*/
|
|
109
|
+
allowedToDependOn;
|
|
110
|
+
static SCHEMA = {
|
|
111
|
+
name: new rules_sdk_1.FieldDef('string'),
|
|
112
|
+
allowedToDependOn: new rules_sdk_1.FieldDef('string[]'),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
exports.ProductDependencyEntry = ProductDependencyEntry;
|
|
116
|
+
/**
|
|
117
|
+
* `product-tags` (#1179, #1204) — two halves behind ONE switch:
|
|
118
|
+
* - every project whose role is in `roles` carries at least one well-formed `product:<name>` nx tag
|
|
119
|
+
* (lowercase kebab, several allowed). The product filter and the Product color mode of
|
|
120
|
+
* `arch:visualize` / `arch:visualize-runtime` seed a product's membership from exactly those tags,
|
|
121
|
+
* so an untagged server, client or app silently drops out of every product view;
|
|
122
|
+
* - every edge of the DEPENDENCY graph respects `productDependencies`: an edge A -> B is legal when,
|
|
123
|
+
* for EVERY product p of A, B carries p or a product p's entry allows. Membership is the resolved
|
|
124
|
+
* one (a project's own tags are final; an untagged project inherits). The runtime graph is not
|
|
125
|
+
* checked.
|
|
103
126
|
*
|
|
104
|
-
* - `mode` — OFF | RUN_EVERY_TIME (a graph rule: it reads every project, every run).
|
|
127
|
+
* - `mode` — OFF | RUN_EVERY_TIME (a graph rule: it reads every project, every run). OFF turns off
|
|
128
|
+
* both halves.
|
|
105
129
|
* - `roles` — required, non-empty: WHICH roles are refused without a tag, e.g.
|
|
106
130
|
* `["server", "client", "app"]`. It decides which projects fail, so it is behaviour, never a default.
|
|
131
|
+
* - `productDependencies` — required; `[]` is valid and means no product may depend on another. One
|
|
132
|
+
* entry per product that DOES depend on others, e.g.
|
|
133
|
+
* `[{ "name": "example", "allowedToDependOn": ["platform", "openapi"] }]`. A product with no entry
|
|
134
|
+
* may depend only on itself. A `name` or `allowedToDependOn` value that no `product:` tag uses, and a
|
|
135
|
+
* duplicate `name`, fail generation naming it — a typo can never silently allow or forbid nothing.
|
|
107
136
|
*/
|
|
108
137
|
class ProductTagsConfig extends rules_sdk_1.BaseRuleConfig {
|
|
109
138
|
roles;
|
|
139
|
+
productDependencies;
|
|
110
140
|
static SCHEMA = {
|
|
111
141
|
mode: new rules_sdk_1.FieldDef('string', rule_configs_1.STRUCTURAL_MODES),
|
|
112
142
|
roles: rules_sdk_1.FieldDef.nonEmptyStrings(),
|
|
143
|
+
productDependencies: rules_sdk_1.FieldDef.objects(ProductDependencyEntry.SCHEMA),
|
|
113
144
|
...rules_sdk_1.BASE_RULE_SCHEMA,
|
|
114
145
|
};
|
|
115
146
|
}
|
|
@@ -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;AAED
|
|
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;;;GAGG;AACH,MAAa,sBAAsB;IAC/B,wGAAwG;IACxG,IAAI,CAAU;IACd;;;OAGG;IACH,iBAAiB,CAAY;IAE7B,MAAM,CAAU,MAAM,GAAwC;QAC1D,IAAI,EAAE,IAAI,oBAAQ,CAAC,QAAQ,CAAC;QAC5B,iBAAiB,EAAE,IAAI,oBAAQ,CAAC,UAAU,CAAC;KAC9C,CAAC;;AAZN,wDAaC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAa,iBAAkB,SAAQ,0BAAc;IAEjD,KAAK,CAAY;IACjB,mBAAmB,CAA4B;IAE/C,MAAM,CAAU,MAAM,GAAmC;QACrD,IAAI,EAAE,IAAI,oBAAQ,CAAC,QAAQ,EAAE,+BAAgB,CAAC;QAC9C,KAAK,EAAE,oBAAQ,CAAC,eAAe,EAAE;QACjC,mBAAmB,EAAE,oBAAQ,CAAC,OAAO,CAAC,sBAAsB,CAAC,MAAM,CAAC;QACpE,GAAG,4BAAgB;KACtB,CAAC;;AAVN,8CAWC","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 * One row of the product dependency matrix (#1204): the OTHER products a product may depend on.\n * Data-only.\n */\nexport class ProductDependencyEntry {\n /** The product this row is about — the name after `product:` in some project's tags, e.g. `example`. */\n name!: string;\n /**\n * The other products a project of `name` may depend on, e.g. `[\"platform\", \"openapi\"]`. A product\n * may always depend on itself, so it is never listed; `[]` states that it depends on no other.\n */\n allowedToDependOn!: string[];\n\n static readonly SCHEMA: SchemaShape<ProductDependencyEntry> = {\n name: new FieldDef('string'),\n allowedToDependOn: new FieldDef('string[]'),\n };\n}\n\n/**\n * `product-tags` (#1179, #1204) — two halves behind ONE switch:\n * - every project whose role is in `roles` carries at least one well-formed `product:<name>` nx tag\n * (lowercase kebab, several allowed). The product filter and the Product color mode of\n * `arch:visualize` / `arch:visualize-runtime` seed a product's membership from exactly those tags,\n * so an untagged server, client or app silently drops out of every product view;\n * - every edge of the DEPENDENCY graph respects `productDependencies`: an edge A -> B is legal when,\n * for EVERY product p of A, B carries p or a product p's entry allows. Membership is the resolved\n * one (a project's own tags are final; an untagged project inherits). The runtime graph is not\n * checked.\n *\n * - `mode` — OFF | RUN_EVERY_TIME (a graph rule: it reads every project, every run). OFF turns off\n * both halves.\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 * - `productDependencies` — required; `[]` is valid and means no product may depend on another. One\n * entry per product that DOES depend on others, e.g.\n * `[{ \"name\": \"example\", \"allowedToDependOn\": [\"platform\", \"openapi\"] }]`. A product with no entry\n * may depend only on itself. A `name` or `allowedToDependOn` value that no `product:` tag uses, and a\n * duplicate `name`, fail generation naming it — a typo can never silently allow or forbid nothing.\n */\nexport class ProductTagsConfig extends BaseRuleConfig {\n declare mode?: StructuralMode;\n roles!: string[];\n productDependencies!: ProductDependencyEntry[];\n\n static readonly SCHEMA: SchemaShape<ProductTagsConfig> = {\n mode: new FieldDef('string', STRUCTURAL_MODES),\n roles: FieldDef.nonEmptyStrings(),\n productDependencies: FieldDef.objects(ProductDependencyEntry.SCHEMA),\n ...BASE_RULE_SCHEMA,\n };\n}\n"]}
|
|
@@ -148,9 +148,9 @@ class ArchitectureGenerator {
|
|
|
148
148
|
const scanned = scanApiRelations(workspaceRoot, enhancedGraph, projectInfos, graphPath);
|
|
149
149
|
const apiContracts = scanned.apiContracts;
|
|
150
150
|
saveApprovedGraph(workspaceRoot, graphPath ?? graph_loader_2.DEFAULT_GRAPH_PATH, enhancedGraph, scanned);
|
|
151
|
-
// Step 4b:
|
|
152
|
-
|
|
153
|
-
const vizPaths = this.visualizer.writeVisualization(enhancedGraph, workspaceRoot);
|
|
151
|
+
// Step 4b: the committed, clickable HTML view, regenerated in lock-step with dependencies.json.
|
|
152
|
+
const productDependencies = tag_truth_1.TagTruthRules.fromConfig(workspaceRoot).productTags?.dependencies ?? null;
|
|
153
|
+
const vizPaths = this.visualizer.writeVisualization(enhancedGraph, workspaceRoot, productDependencies);
|
|
154
154
|
console.log(`✅ Wrote ${vizPaths.htmlPath}`);
|
|
155
155
|
// Step 4c: The page's Impact mode — nx affected for "Changed on this branch" ONLY, into a
|
|
156
156
|
// gitignored sidecar beside the html (never into the committed files). Generate never scans
|
|
@@ -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 type { RemoteServices } from '../../lib/remote-services';\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 remoteServices: RemoteServices,\n): void {\n console.log('📡 Deriving runtime graph from dependencies.json (implements × uses per API)...');\n const report = deriveRuntimeGraphReport(\n graph,\n hiddenProjects,\n apiContracts,\n externalSystems,\n remoteServices,\n );\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 saved.remoteServices,\n );\n}\n\n/** The 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 /** Reviewed remote services (#1203): read from the saved file, written back verbatim. */\n public readonly remoteServices: RemoteServices,\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 scanned.remoteServices,\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 scanned.remoteServices,\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":"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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, TagTruthRules } 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 type { RemoteServices } from '../../lib/remote-services';\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 remoteServices: RemoteServices,\n): void {\n console.log('📡 Deriving runtime graph from dependencies.json (implements × uses per API)...');\n const report = deriveRuntimeGraphReport(\n graph,\n hiddenProjects,\n apiContracts,\n externalSystems,\n remoteServices,\n );\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 saved.remoteServices,\n );\n}\n\n/** The 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 /** Reviewed remote services (#1203): read from the saved file, written back verbatim. */\n public readonly remoteServices: RemoteServices,\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: the committed, clickable HTML view, regenerated in lock-step with dependencies.json.\n const productDependencies = TagTruthRules.fromConfig(workspaceRoot).productTags?.dependencies ?? null;\n const vizPaths = this.visualizer.writeVisualization(enhancedGraph, workspaceRoot, productDependencies);\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 scanned.remoteServices,\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 scanned.remoteServices,\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"]}
|
|
@@ -22,6 +22,7 @@ const path = tslib_1.__importStar(require("path"));
|
|
|
22
22
|
const saved_snapshot_1 = require("../../lib/saved-snapshot");
|
|
23
23
|
const graph_loader_1 = require("../../lib/graph-loader");
|
|
24
24
|
const graph_visualizer_1 = require("../../lib/graph-visualizer");
|
|
25
|
+
const tag_truth_1 = require("../../lib/tag-truth");
|
|
25
26
|
const graph_impact_1 = require("../../lib/graph-impact");
|
|
26
27
|
const rules_config_1 = require("@webpieces/rules-config");
|
|
27
28
|
const toError_1 = require("../../toError");
|
|
@@ -45,7 +46,8 @@ class SavedGraphViewer {
|
|
|
45
46
|
console.log('📂 Loading saved graph...');
|
|
46
47
|
const graph = this.snapshot.loadProjects(workspaceRoot, graphPath).projects;
|
|
47
48
|
console.log('🎨 Generating visualization...');
|
|
48
|
-
const
|
|
49
|
+
const productDependencies = tag_truth_1.TagTruthRules.fromConfig(workspaceRoot).productTags?.dependencies ?? null;
|
|
50
|
+
const vizPaths = this.visualizer.writeVisualization(graph, workspaceRoot, productDependencies);
|
|
49
51
|
console.log(`✅ Generated: ${vizPaths.htmlPath}`);
|
|
50
52
|
// Impact is optional: it reports, it never throws, and it never touches committed files.
|
|
51
53
|
console.log('🟠 Refreshing impact (nx affected: changed on this branch, and since the last commit)...');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/visualize/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/visualize/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Visualize Executor\n *\n * Renders the SAVED architecture graph (DOT + HTML), refreshes the Impact sidecar, and opens the\n * visualization in a browser. It never regenerates the graph: the architecture facts stay the saved\n * snapshot. Impact, though, is per-checkout and gitignored (graph-impact.ts) — it is exactly the thing\n * that goes stale when you switch branches or check out a commit — so viewing refreshes it against\n * the saved graph, with one `nx show projects --affected --base=<base>` scan per comparison that\n * exists here: \"Changed on this branch\" (fork point → working tree) and \"Last commit\" (HEAD^ →\n * working tree). On main or a detached HEAD only the last commit exists, so checking out a commit\n * and running `pnpm arch:visualize` shows what that commit changed.\n *\n * Usage:\n * nx run architecture:visualize (pnpm arch:visualize)\n */\n\nimport * as path from 'path';\nimport type { ExecutorContext } from '@nx/devkit';\nimport { SavedSnapshot } from '../../lib/saved-snapshot';\nimport { DEFAULT_GRAPH_PATH } from '../../lib/graph-loader';\nimport { GraphVisualizer } from '../../lib/graph-visualizer';\nimport { TagTruthRules } from '../../lib/tag-truth';\nimport { ImpactRefresh, VISUALIZE_IMPACT_KINDS } from '../../lib/graph-impact';\nimport { RuleFailError, renderRuleFailForHuman } from '@webpieces/rules-config';\nimport { toError } from '../../toError';\n\nexport interface VisualizeExecutorOptions {\n graphPath?: string;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\n/** The view pipeline: load the saved graph, write the page, refresh Impact, open the page. */\nexport class SavedGraphViewer {\n constructor(\n private readonly visualizer: GraphVisualizer = new GraphVisualizer(),\n private readonly impact: ImpactRefresh = new ImpactRefresh(),\n private readonly snapshot: SavedSnapshot = new SavedSnapshot(),\n ) {}\n\n async view(options: VisualizeExecutorOptions, context: ExecutorContext): Promise<ExecutorResult> {\n const graphPath = options.graphPath ?? DEFAULT_GRAPH_PATH;\n const workspaceRoot = context.root;\n\n console.log('\\n🎨 Architecture Visualization\\n');\n console.log(this.snapshot.message());\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- Nx command rendering boundary; success:false propagates failure to Nx\n try {\n console.log('📂 Loading saved graph...');\n const graph = this.snapshot.loadProjects(workspaceRoot, graphPath).projects;\n\n console.log('🎨 Generating visualization...');\n const productDependencies = TagTruthRules.fromConfig(workspaceRoot).productTags?.dependencies ?? null;\n const vizPaths = this.visualizer.writeVisualization(graph, workspaceRoot, productDependencies);\n console.log(`✅ Generated: ${vizPaths.htmlPath}`);\n\n // Impact is optional: it reports, it never throws, and it never touches committed files.\n console.log('🟠 Refreshing impact (nx affected: changed on this branch, and since the last commit)...');\n console.log(await this.impact.run(path.dirname(vizPaths.htmlPath), workspaceRoot, graph, VISUALIZE_IMPACT_KINDS));\n\n console.log('\\n🌐 Opening visualization in browser...');\n if (this.visualizer.openVisualization(vizPaths.htmlPath)) {\n console.log('✅ Browser opened');\n } else {\n console.log(`⚠️ Could not auto-open. Open manually: ${vizPaths.htmlPath}`);\n }\n\n return { success: true };\n } catch (err: unknown) {\n const error = toError(err);\n const rendered = error instanceof RuleFailError ? renderRuleFailForHuman(error) : error.message;\n console.error(`❌ Visualization failed for ${graphPath}:`, rendered);\n return { success: false };\n }\n }\n}\n\n// webpieces-disable no-function-outside-class -- nx executor module: nx resolves a default-export function here\nexport default async function runExecutor(\n options: VisualizeExecutorOptions,\n context: ExecutorContext\n): Promise<ExecutorResult> {\n return new SavedGraphViewer().view(options, context);\n}\n"]}
|
package/src/index.d.ts
CHANGED
|
@@ -25,9 +25,10 @@ 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, ProductTagsConfig } from './configs/tag-truth-configs';
|
|
28
|
+
export { ApiClientPackagesEntry, ApiLibDependenciesConfig, ApiLibPathConfig, FrameworkFolderEntry, FrameworkFolderConfig, ProductDependencyEntry, ProductTagsConfig } from './configs/tag-truth-configs';
|
|
29
29
|
export { GraphRenderModel, RenderNode, RenderEdge, NodeTags } from './lib/graph-render-model';
|
|
30
30
|
export { GraphMode, NodeModeDots, LegendFacts, ProductCount } from './lib/graph-color-modes';
|
|
31
|
-
export { ProductMembership, RuntimeProductMembership, ProductPalette, ProductColor } from './lib/graph-products';
|
|
31
|
+
export { ProductMembership, RuntimeProductMembership, ProductPalette, ProductColor, ProductBoundary } from './lib/graph-products';
|
|
32
|
+
export { ProductDependencyMatrix } from './lib/product-dependencies';
|
|
32
33
|
export { ProductResolver, ProductResolution, PRODUCT_TAG_PREFIX } from './lib/product-resolver';
|
|
33
34
|
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.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;
|
|
3
|
+
exports.EntryPointLevels = exports.PRODUCT_TAG_PREFIX = exports.ProductResolution = exports.ProductResolver = exports.ProductDependencyMatrix = exports.ProductBoundary = 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.ProductDependencyEntry = 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,6 +45,7 @@ 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, "ProductDependencyEntry", { enumerable: true, get: function () { return tag_truth_configs_1.ProductDependencyEntry; } });
|
|
48
49
|
Object.defineProperty(exports, "ProductTagsConfig", { enumerable: true, get: function () { return tag_truth_configs_1.ProductTagsConfig; } });
|
|
49
50
|
var graph_render_model_1 = require("./lib/graph-render-model");
|
|
50
51
|
Object.defineProperty(exports, "GraphRenderModel", { enumerable: true, get: function () { return graph_render_model_1.GraphRenderModel; } });
|
|
@@ -61,6 +62,9 @@ Object.defineProperty(exports, "ProductMembership", { enumerable: true, get: fun
|
|
|
61
62
|
Object.defineProperty(exports, "RuntimeProductMembership", { enumerable: true, get: function () { return graph_products_1.RuntimeProductMembership; } });
|
|
62
63
|
Object.defineProperty(exports, "ProductPalette", { enumerable: true, get: function () { return graph_products_1.ProductPalette; } });
|
|
63
64
|
Object.defineProperty(exports, "ProductColor", { enumerable: true, get: function () { return graph_products_1.ProductColor; } });
|
|
65
|
+
Object.defineProperty(exports, "ProductBoundary", { enumerable: true, get: function () { return graph_products_1.ProductBoundary; } });
|
|
66
|
+
var product_dependencies_1 = require("./lib/product-dependencies");
|
|
67
|
+
Object.defineProperty(exports, "ProductDependencyMatrix", { enumerable: true, get: function () { return product_dependencies_1.ProductDependencyMatrix; } });
|
|
64
68
|
var product_resolver_1 = require("./lib/product-resolver");
|
|
65
69
|
Object.defineProperty(exports, "ProductResolver", { enumerable: true, get: function () { return product_resolver_1.ProductResolver; } });
|
|
66
70
|
Object.defineProperty(exports, "ProductResolution", { enumerable: true, get: function () { return product_resolver_1.ProductResolution; } });
|
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,
|
|
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,iEAAyM;AAAhM,2HAAA,sBAAsB,OAAA;AAAE,6HAAA,wBAAwB,OAAA;AAAE,qHAAA,gBAAgB,OAAA;AAAE,yHAAA,oBAAoB,OAAA;AAAE,0HAAA,qBAAqB,OAAA;AAAE,2HAAA,sBAAsB,OAAA;AAAE,sHAAA,iBAAiB,OAAA;AAEnK,+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,uDAAkI;AAAzH,mHAAA,iBAAiB,OAAA;AAAE,0HAAA,wBAAwB,OAAA;AAAE,gHAAA,cAAc,OAAA;AAAE,8GAAA,YAAY,OAAA;AAAE,iHAAA,eAAe,OAAA;AACnG,mEAAqE;AAA5D,+HAAA,uBAAuB,OAAA;AAChC,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, ProductDependencyEntry, 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, ProductBoundary } from './lib/graph-products';\nexport { ProductDependencyMatrix } from './lib/product-dependencies';\nexport { ProductResolver, ProductResolution, PRODUCT_TAG_PREFIX } from './lib/product-resolver';\nexport { EntryPointLevels } from './lib/graph-entry-levels';\n"]}
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
*/
|
|
32
32
|
import { KnownFramework } from '@webpieces/rules-sdk';
|
|
33
33
|
import { ProductPalette } from './graph-products';
|
|
34
|
+
import type { ProductDependencyEntry } from '../configs/tag-truth-configs';
|
|
34
35
|
export declare enum GraphMode {
|
|
35
36
|
RUNTIME = "runtime",
|
|
36
37
|
ARCHITECTURE = "architecture",
|
|
@@ -147,6 +148,13 @@ export declare class LegendFacts {
|
|
|
147
148
|
multiProduct: boolean;
|
|
148
149
|
/** Some product is past the palette's eight and is painted with the neutral fill. */
|
|
149
150
|
productOverflow: boolean;
|
|
151
|
+
/** Dependency edges crossing into another product (#1204), drawn dashed in the Product mode. */
|
|
152
|
+
crossProductEdges: number;
|
|
153
|
+
/**
|
|
154
|
+
* The `product-tags` rule's matrix (#1204), listed in the Product legend; null when the rule is
|
|
155
|
+
* OFF or the page was drawn without it.
|
|
156
|
+
*/
|
|
157
|
+
productDependencies: ProductDependencyEntry[] | null;
|
|
150
158
|
}
|
|
151
159
|
export declare class NodeModeStyler {
|
|
152
160
|
styleOf(framework: string): FrameworkStyle | undefined;
|
|
@@ -227,6 +227,13 @@ class LegendFacts {
|
|
|
227
227
|
multiProduct = false;
|
|
228
228
|
/** Some product is past the palette's eight and is painted with the neutral fill. */
|
|
229
229
|
productOverflow = false;
|
|
230
|
+
/** Dependency edges crossing into another product (#1204), drawn dashed in the Product mode. */
|
|
231
|
+
crossProductEdges = 0;
|
|
232
|
+
/**
|
|
233
|
+
* The `product-tags` rule's matrix (#1204), listed in the Product legend; null when the rule is
|
|
234
|
+
* OFF or the page was drawn without it.
|
|
235
|
+
*/
|
|
236
|
+
productDependencies = null;
|
|
230
237
|
}
|
|
231
238
|
exports.LegendFacts = LegendFacts;
|
|
232
239
|
class NodeModeStyler {
|