@webpieces/nx-webpieces-rules 0.4.846 → 0.4.848
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 +54 -15
- package/executors.json +3 -68
- package/package.json +9 -9
- package/src/executors/help/executor.js +6 -4
- package/src/executors/help/executor.js.map +1 -1
- package/src/executors/validate-architecture-unchanged/executor.d.ts +8 -0
- package/src/executors/validate-architecture-unchanged/executor.js +20 -18
- package/src/executors/validate-architecture-unchanged/executor.js.map +1 -1
- package/src/executors/validate-code/executor.d.ts +2 -7
- package/src/executors/validate-code/executor.js +31 -2
- package/src/executors/validate-code/executor.js.map +1 -1
- package/src/executors/validate-code/schema.json +6 -281
- package/src/executors/validate-runtime-architecture/schema.json +1 -1
- package/src/executors/visualize/executor.js +7 -9
- package/src/executors/visualize/executor.js.map +1 -1
- package/src/executors/visualize/schema.json +1 -1
- package/src/executors/visualize-runtime/executor.js +8 -9
- package/src/executors/visualize-runtime/executor.js.map +1 -1
- package/src/executors/visualize-runtime/schema.json +1 -1
- package/src/lib/api-usage/wire-closure.d.ts +3 -3
- package/src/lib/api-usage/wire-closure.js +5 -5
- package/src/lib/api-usage/wire-closure.js.map +1 -1
- package/src/lib/api-usage/wire-suffix-entry.d.ts +5 -0
- package/src/lib/api-usage/wire-suffix-entry.js +10 -0
- package/src/lib/api-usage/wire-suffix-entry.js.map +1 -0
- package/src/lib/graph-loader.js +2 -2
- package/src/lib/graph-loader.js.map +1 -1
- package/src/lib/graph-visualizer.d.ts +1 -0
- package/src/lib/graph-visualizer.js +3 -0
- package/src/lib/graph-visualizer.js.map +1 -1
- package/src/lib/rule-gate.d.ts +1 -2
- package/src/lib/rule-gate.js +3 -4
- package/src/lib/rule-gate.js.map +1 -1
- package/src/lib/runtime-html-page.d.ts +35 -0
- package/src/lib/runtime-html-page.js +112 -0
- package/src/lib/runtime-html-page.js.map +1 -0
- package/src/lib/runtime-visualizer.d.ts +4 -38
- package/src/lib/runtime-visualizer.js +5 -108
- package/src/lib/runtime-visualizer.js.map +1 -1
- package/src/lib/saved-snapshot.d.ts +16 -0
- package/src/lib/saved-snapshot.js +80 -0
- package/src/lib/saved-snapshot.js.map +1 -0
- package/src/native-actions.d.ts +23 -0
- package/src/native-actions.js +69 -0
- package/src/native-actions.js.map +1 -0
- package/src/nx-policy-registry.d.ts +8 -0
- package/src/nx-policy-registry.js +40 -0
- package/src/nx-policy-registry.js.map +1 -0
- package/src/pack-build-runner.d.ts +6 -0
- package/src/pack-build-runner.js +46 -0
- package/src/pack-build-runner.js.map +1 -0
- package/src/plugin.js +8 -42
- package/src/plugin.js.map +1 -1
- package/src/rule-help.d.ts +3 -0
- package/src/rule-help.js +24 -0
- package/src/rule-help.js.map +1 -0
- package/src/rule-pack.js +5 -28
- package/src/rule-pack.js.map +1 -1
- package/src/rule-runtime.d.ts +5 -0
- package/src/rule-runtime.js +94 -0
- package/src/rule-runtime.js.map +1 -0
- package/src/runtime-targets.js +2 -2
- package/src/runtime-targets.js.map +1 -1
- package/src/validation-targets.js +21 -5
- package/src/validation-targets.js.map +1 -1
- package/src/executors/validate-catch-error-pattern/executor.d.ts +0 -4
- package/src/executors/validate-catch-error-pattern/executor.js +0 -8
- package/src/executors/validate-catch-error-pattern/executor.js.map +0 -1
- package/src/executors/validate-catch-error-pattern/schema.json +0 -24
- package/src/executors/validate-dtos/executor.d.ts +0 -4
- package/src/executors/validate-dtos/executor.js +0 -8
- package/src/executors/validate-dtos/executor.js.map +0 -1
- package/src/executors/validate-dtos/schema.json +0 -33
- package/src/executors/validate-modified-files/executor.d.ts +0 -4
- package/src/executors/validate-modified-files/executor.js +0 -8
- package/src/executors/validate-modified-files/executor.js.map +0 -1
- package/src/executors/validate-modified-files/schema.json +0 -25
- package/src/executors/validate-modified-methods/executor.d.ts +0 -4
- package/src/executors/validate-modified-methods/executor.js +0 -8
- package/src/executors/validate-modified-methods/executor.js.map +0 -1
- package/src/executors/validate-modified-methods/schema.json +0 -25
- package/src/executors/validate-new-methods/executor.d.ts +0 -4
- package/src/executors/validate-new-methods/executor.js +0 -8
- package/src/executors/validate-new-methods/executor.js.map +0 -1
- package/src/executors/validate-new-methods/schema.json +0 -25
- package/src/executors/validate-no-any-unknown/executor.d.ts +0 -4
- package/src/executors/validate-no-any-unknown/executor.js +0 -8
- package/src/executors/validate-no-any-unknown/executor.js.map +0 -1
- package/src/executors/validate-no-any-unknown/schema.json +0 -24
- package/src/executors/validate-no-destructure/executor.d.ts +0 -4
- package/src/executors/validate-no-destructure/executor.js +0 -8
- package/src/executors/validate-no-destructure/executor.js.map +0 -1
- package/src/executors/validate-no-destructure/schema.json +0 -24
- package/src/executors/validate-no-direct-api-resolver/executor.d.ts +0 -4
- package/src/executors/validate-no-direct-api-resolver/executor.js +0 -8
- package/src/executors/validate-no-direct-api-resolver/executor.js.map +0 -1
- package/src/executors/validate-no-direct-api-resolver/schema.json +0 -29
- package/src/executors/validate-no-implicit-any/executor.d.ts +0 -4
- package/src/executors/validate-no-implicit-any/executor.js +0 -8
- package/src/executors/validate-no-implicit-any/executor.js.map +0 -1
- package/src/executors/validate-no-implicit-any/schema.json +0 -24
- package/src/executors/validate-no-inline-types/executor.d.ts +0 -4
- package/src/executors/validate-no-inline-types/executor.js +0 -8
- package/src/executors/validate-no-inline-types/executor.js.map +0 -1
- package/src/executors/validate-no-inline-types/schema.json +0 -24
- package/src/executors/validate-no-unmanaged-exceptions/executor.d.ts +0 -4
- package/src/executors/validate-no-unmanaged-exceptions/executor.js +0 -8
- package/src/executors/validate-no-unmanaged-exceptions/executor.js.map +0 -1
- package/src/executors/validate-no-unmanaged-exceptions/schema.json +0 -24
- package/src/executors/validate-prisma-converters/executor.d.ts +0 -4
- package/src/executors/validate-prisma-converters/executor.js +0 -8
- package/src/executors/validate-prisma-converters/executor.js.map +0 -1
- package/src/executors/validate-prisma-converters/schema.json +0 -38
- package/src/executors/validate-return-types/executor.d.ts +0 -4
- package/src/executors/validate-return-types/executor.js +0 -8
- package/src/executors/validate-return-types/executor.js.map +0 -1
- package/src/executors/validate-return-types/schema.json +0 -24
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/schema",
|
|
3
3
|
"title": "Validate Code Executor",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "Execute client-declared build contributions. Every policy option comes from its owner config; these flags request an ephemeral single-rule debug run.",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"properties": {
|
|
7
7
|
"rule": {
|
|
@@ -14,287 +14,12 @@
|
|
|
14
14
|
},
|
|
15
15
|
"projects": {
|
|
16
16
|
"type": ["string", "array"],
|
|
17
|
-
"items": {
|
|
17
|
+
"items": {
|
|
18
|
+
"type": "string"
|
|
19
|
+
},
|
|
18
20
|
"description": "DEBUG run: judge only files owned by these nx projects, comma-separated, e.g. lang-apis,lang-fsdb-api. Prints a site count per project."
|
|
19
|
-
},
|
|
20
|
-
"methodMaxLimit": {
|
|
21
|
-
"type": "object",
|
|
22
|
-
"description": "Configuration for method line limit validation",
|
|
23
|
-
"properties": {
|
|
24
|
-
"limit": {
|
|
25
|
-
"type": "number",
|
|
26
|
-
"description": "Maximum lines allowed for methods",
|
|
27
|
-
"default": 80
|
|
28
|
-
},
|
|
29
|
-
"mode": {
|
|
30
|
-
"type": "string",
|
|
31
|
-
"description": "OFF: skip validation. NEW_METHODS: only new methods in diff. NEW_AND_MODIFIED_METHODS: new methods + methods with changes. NEW_AND_MODIFIED_FILES: all methods in modified files.",
|
|
32
|
-
"default": "NEW_AND_MODIFIED_METHODS"
|
|
33
|
-
},
|
|
34
|
-
"disableAllowed": {
|
|
35
|
-
"type": "boolean",
|
|
36
|
-
"description": "Whether disable comments work. When false, no escape hatch (like old STRICT mode).",
|
|
37
|
-
"default": true
|
|
38
|
-
},
|
|
39
|
-
"turnOffRuleUntilEpoch": {
|
|
40
|
-
"type": "number",
|
|
41
|
-
"description": "Epoch seconds. Until this time, skip modified-method validation (new methods still enforced). When expired, normal mode resumes. Omit when not needed."
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
"fileMaxLimit": {
|
|
46
|
-
"type": "object",
|
|
47
|
-
"description": "Configuration for file line limit validation",
|
|
48
|
-
"properties": {
|
|
49
|
-
"limit": {
|
|
50
|
-
"type": "number",
|
|
51
|
-
"description": "Maximum lines for modified files",
|
|
52
|
-
"default": 900
|
|
53
|
-
},
|
|
54
|
-
"mode": {
|
|
55
|
-
"type": "string",
|
|
56
|
-
"description": "OFF: skip validation. NEW_AND_MODIFIED_FILES: all code in modified files.",
|
|
57
|
-
"default": "NEW_AND_MODIFIED_FILES"
|
|
58
|
-
},
|
|
59
|
-
"disableAllowed": {
|
|
60
|
-
"type": "boolean",
|
|
61
|
-
"description": "Whether disable comments work. When false, no escape hatch (like old STRICT mode).",
|
|
62
|
-
"default": true
|
|
63
|
-
},
|
|
64
|
-
"turnOffRuleUntilEpoch": {
|
|
65
|
-
"type": "number",
|
|
66
|
-
"description": "Epoch seconds. Until this time, skip modified-file validation. When expired, normal mode resumes. Omit when not needed."
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
"requireReturnType": {
|
|
71
|
-
"type": "object",
|
|
72
|
-
"description": "Configuration for return type validation",
|
|
73
|
-
"properties": {
|
|
74
|
-
"mode": {
|
|
75
|
-
"type": "string",
|
|
76
|
-
"description": "OFF: skip return type validation. NEW_METHODS: only new methods in diff. NEW_AND_MODIFIED_METHODS: new methods + methods with changes. NEW_AND_MODIFIED_FILES: all methods in modified files.",
|
|
77
|
-
"default": "OFF"
|
|
78
|
-
},
|
|
79
|
-
"disableAllowed": {
|
|
80
|
-
"type": "boolean",
|
|
81
|
-
"description": "Whether disable comments work. When false, no escape hatch.",
|
|
82
|
-
"default": true
|
|
83
|
-
},
|
|
84
|
-
"turnOffRuleUntilEpoch": {
|
|
85
|
-
"type": "number",
|
|
86
|
-
"description": "Epoch seconds. Until this time, skip validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
"noInlineTypeLiterals": {
|
|
91
|
-
"type": "object",
|
|
92
|
-
"description": "Configuration for no-inline-types validation",
|
|
93
|
-
"properties": {
|
|
94
|
-
"mode": {
|
|
95
|
-
"type": "string",
|
|
96
|
-
"description": "OFF: skip validation. NEW_METHODS: only new methods. NEW_AND_MODIFIED_METHODS: new + modified methods. NEW_AND_MODIFIED_FILES: all in modified files. Disallows inline type literals like { x: number } - use named types instead.",
|
|
97
|
-
"default": "OFF"
|
|
98
|
-
},
|
|
99
|
-
"disableAllowed": {
|
|
100
|
-
"type": "boolean",
|
|
101
|
-
"description": "Whether disable comments work. When false, no escape hatch.",
|
|
102
|
-
"default": true
|
|
103
|
-
},
|
|
104
|
-
"turnOffRuleUntilEpoch": {
|
|
105
|
-
"type": "number",
|
|
106
|
-
"description": "Epoch seconds. Until this time, skip validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
},
|
|
110
|
-
"noAnyUnknown": {
|
|
111
|
-
"type": "object",
|
|
112
|
-
"description": "Configuration for no-any-unknown validation",
|
|
113
|
-
"properties": {
|
|
114
|
-
"mode": {
|
|
115
|
-
"type": "string",
|
|
116
|
-
"description": "OFF: skip validation. NEW_AND_MODIFIED_CODE: only changed lines in diff. NEW_AND_MODIFIED_FILES: all in modified files. Disallows `any` and `unknown` TypeScript keywords.",
|
|
117
|
-
"default": "OFF"
|
|
118
|
-
},
|
|
119
|
-
"disableAllowed": {
|
|
120
|
-
"type": "boolean",
|
|
121
|
-
"description": "Whether disable comments work. When false, no escape hatch.",
|
|
122
|
-
"default": true
|
|
123
|
-
},
|
|
124
|
-
"turnOffRuleUntilEpoch": {
|
|
125
|
-
"type": "number",
|
|
126
|
-
"description": "Epoch seconds. Until this time, skip validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
},
|
|
130
|
-
"noImplicitAny": {
|
|
131
|
-
"type": "object",
|
|
132
|
-
"description": "Configuration for no-implicit-any validation (TS7006/7005/7018/etc via the TypeScript compiler).",
|
|
133
|
-
"properties": {
|
|
134
|
-
"mode": {
|
|
135
|
-
"type": "string",
|
|
136
|
-
"description": "OFF: skip validation. NEW_AND_MODIFIED_CODE: only implicit-any on changed lines. NEW_AND_MODIFIED_FILES: all implicit-any in modified files.",
|
|
137
|
-
"default": "OFF"
|
|
138
|
-
},
|
|
139
|
-
"disableAllowed": {
|
|
140
|
-
"type": "boolean",
|
|
141
|
-
"description": "Whether // webpieces-disable no-implicit-any comments work. When false, no escape hatch.",
|
|
142
|
-
"default": true
|
|
143
|
-
},
|
|
144
|
-
"turnOffRuleUntilEpoch": {
|
|
145
|
-
"type": "number",
|
|
146
|
-
"description": "Epoch seconds. Until this time, skip validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
},
|
|
150
|
-
"validateDtos": {
|
|
151
|
-
"type": "object",
|
|
152
|
-
"description": "Validate DTO fields match Prisma Dbo model fields. Prevents AI from inventing field names.",
|
|
153
|
-
"properties": {
|
|
154
|
-
"mode": {
|
|
155
|
-
"type": "string",
|
|
156
|
-
"description": "OFF: skip validation. MODIFIED_CLASS: only validate Dto classes with changed lines. NEW_AND_MODIFIED_FILES: validate all Dtos in modified files.",
|
|
157
|
-
"default": "OFF"
|
|
158
|
-
},
|
|
159
|
-
"disableAllowed": {
|
|
160
|
-
"type": "boolean",
|
|
161
|
-
"description": "Whether @deprecated field exemption works. When false, all fields must match.",
|
|
162
|
-
"default": true
|
|
163
|
-
},
|
|
164
|
-
"prismaSchemaPath": {
|
|
165
|
-
"type": "string",
|
|
166
|
-
"description": "Relative path from workspace root to schema.prisma"
|
|
167
|
-
},
|
|
168
|
-
"dtoSourcePaths": {
|
|
169
|
-
"type": "array",
|
|
170
|
-
"items": { "type": "string" },
|
|
171
|
-
"description": "Array of directories (relative to workspace root) containing Dto files"
|
|
172
|
-
},
|
|
173
|
-
"turnOffRuleUntilEpoch": {
|
|
174
|
-
"type": "number",
|
|
175
|
-
"description": "Epoch seconds. Until this time, skip DTO validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
},
|
|
179
|
-
"prismaConverter": {
|
|
180
|
-
"type": "object",
|
|
181
|
-
"description": "Validate Prisma converter methods follow scalable patterns: correct Dbo parameter, no async, no standalone functions, Dto creation only in converter directories.",
|
|
182
|
-
"properties": {
|
|
183
|
-
"mode": {
|
|
184
|
-
"type": "string",
|
|
185
|
-
"description": "OFF: skip validation. NEW_AND_MODIFIED_METHODS: validate new/modified methods in converters + changed lines in non-converters. NEW_AND_MODIFIED_FILES: validate all methods in modified files.",
|
|
186
|
-
"default": "OFF"
|
|
187
|
-
},
|
|
188
|
-
"disableAllowed": {
|
|
189
|
-
"type": "boolean",
|
|
190
|
-
"description": "Whether disable comments work. When false, no escape hatch.",
|
|
191
|
-
"default": true
|
|
192
|
-
},
|
|
193
|
-
"schemaPath": {
|
|
194
|
-
"type": "string",
|
|
195
|
-
"description": "Relative path from workspace root to schema.prisma"
|
|
196
|
-
},
|
|
197
|
-
"convertersPaths": {
|
|
198
|
-
"type": "array",
|
|
199
|
-
"items": { "type": "string" },
|
|
200
|
-
"description": "Array of directories (relative to workspace root) containing converter files"
|
|
201
|
-
},
|
|
202
|
-
"enforcePaths": {
|
|
203
|
-
"type": "array",
|
|
204
|
-
"items": { "type": "string" },
|
|
205
|
-
"description": "Array of directory prefixes (relative to workspace root) to scope validation to. When set, only files under these paths are checked. When empty/omitted, all changed files are checked."
|
|
206
|
-
},
|
|
207
|
-
"turnOffRuleUntilEpoch": {
|
|
208
|
-
"type": "number",
|
|
209
|
-
"description": "Epoch seconds. Until this time, skip prisma-converter validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
},
|
|
213
|
-
"noDestructure": {
|
|
214
|
-
"type": "object",
|
|
215
|
-
"description": "Validate no destructuring patterns are used. Destructuring hurts code traceability.",
|
|
216
|
-
"properties": {
|
|
217
|
-
"mode": {
|
|
218
|
-
"type": "string",
|
|
219
|
-
"description": "OFF: skip validation. NEW_AND_MODIFIED_CODE: only changed lines in diff. NEW_AND_MODIFIED_FILES: all in modified files. Disallows destructuring patterns.",
|
|
220
|
-
"default": "OFF"
|
|
221
|
-
},
|
|
222
|
-
"disableAllowed": {
|
|
223
|
-
"type": "boolean",
|
|
224
|
-
"description": "Whether disable comments work. When false, no escape hatch.",
|
|
225
|
-
"default": true
|
|
226
|
-
},
|
|
227
|
-
"turnOffRuleUntilEpoch": {
|
|
228
|
-
"type": "number",
|
|
229
|
-
"description": "Epoch seconds. Until this time, skip validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
},
|
|
233
|
-
"catchErrorPattern": {
|
|
234
|
-
"type": "object",
|
|
235
|
-
"description": "Validate catch blocks follow standardized pattern: catch (err: unknown) { const error = toError(err); }",
|
|
236
|
-
"properties": {
|
|
237
|
-
"mode": {
|
|
238
|
-
"type": "string",
|
|
239
|
-
"description": "OFF: skip validation. NEW_AND_MODIFIED_CODE: only catch blocks on changed lines. NEW_AND_MODIFIED_FILES: all catch blocks in modified files.",
|
|
240
|
-
"default": "OFF"
|
|
241
|
-
},
|
|
242
|
-
"disableAllowed": {
|
|
243
|
-
"type": "boolean",
|
|
244
|
-
"description": "Whether disable comments work. When false, no escape hatch.",
|
|
245
|
-
"default": true
|
|
246
|
-
},
|
|
247
|
-
"turnOffRuleUntilEpoch": {
|
|
248
|
-
"type": "number",
|
|
249
|
-
"description": "Epoch seconds. Until this time, skip validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
},
|
|
253
|
-
"noUnmanagedExceptions": {
|
|
254
|
-
"type": "object",
|
|
255
|
-
"description": "Validate try/catch blocks are not used outside chokepoints. Exceptions should bubble to filters/globalErrorHandlers.",
|
|
256
|
-
"properties": {
|
|
257
|
-
"mode": {
|
|
258
|
-
"type": "string",
|
|
259
|
-
"description": "OFF: skip validation. NEW_AND_MODIFIED_CODE: only changed lines in diff. NEW_AND_MODIFIED_FILES: all in modified files. Disallows try/catch blocks.",
|
|
260
|
-
"default": "OFF"
|
|
261
|
-
},
|
|
262
|
-
"disableAllowed": {
|
|
263
|
-
"type": "boolean",
|
|
264
|
-
"description": "Whether disable comments work. When false, no escape hatch.",
|
|
265
|
-
"default": true
|
|
266
|
-
},
|
|
267
|
-
"turnOffRuleUntilEpoch": {
|
|
268
|
-
"type": "number",
|
|
269
|
-
"description": "Epoch seconds. Until this time, skip validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
},
|
|
273
|
-
"noDirectApiInResolver": {
|
|
274
|
-
"type": "object",
|
|
275
|
-
"description": "Validate resolvers use services (not APIs) and components subscribe to service observables (not route.snapshot.data).",
|
|
276
|
-
"properties": {
|
|
277
|
-
"mode": {
|
|
278
|
-
"type": "string",
|
|
279
|
-
"description": "OFF: skip validation. NEW_AND_MODIFIED_CODE: only changed lines in diff. NEW_AND_MODIFIED_METHODS: violations in new/modified method/route scopes. NEW_AND_MODIFIED_FILES: all in modified files.",
|
|
280
|
-
"default": "OFF"
|
|
281
|
-
},
|
|
282
|
-
"disableAllowed": {
|
|
283
|
-
"type": "boolean",
|
|
284
|
-
"description": "Whether disable comments work. When false, no escape hatch.",
|
|
285
|
-
"default": true
|
|
286
|
-
},
|
|
287
|
-
"turnOffRuleUntilEpoch": {
|
|
288
|
-
"type": "number",
|
|
289
|
-
"description": "Epoch seconds. Until this time, skip validation entirely. When expired, normal mode resumes. Omit when not needed."
|
|
290
|
-
},
|
|
291
|
-
"enforcePaths": {
|
|
292
|
-
"type": "array",
|
|
293
|
-
"items": { "type": "string" },
|
|
294
|
-
"description": "Array of directory prefixes (relative to workspace root) to scope validation to. When set, only files under these paths are checked. When empty/omitted, all changed files are checked."
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
21
|
}
|
|
298
22
|
},
|
|
299
|
-
"required": []
|
|
23
|
+
"required": [],
|
|
24
|
+
"additionalProperties": false
|
|
300
25
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/schema",
|
|
3
3
|
"title": "Validate Runtime Architecture Executor",
|
|
4
|
-
"description": "Workspace: validate the runtime microservice graph has no disallowed cycles and matches the committed runtime-dependencies.json. Config from
|
|
4
|
+
"description": "Workspace: validate the runtime microservice graph has no disallowed cycles and matches the committed runtime-dependencies.json. Config from the declared Nx owner file's direct runtime-architecture entry.",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"properties": {},
|
|
7
7
|
"required": []
|
|
@@ -10,24 +10,22 @@
|
|
|
10
10
|
*/
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.default = runExecutor;
|
|
13
|
+
const saved_snapshot_1 = require("../../lib/saved-snapshot");
|
|
13
14
|
const graph_loader_1 = require("../../lib/graph-loader");
|
|
14
15
|
const graph_visualizer_1 = require("../../lib/graph-visualizer");
|
|
15
16
|
const rules_config_1 = require("@webpieces/rules-config");
|
|
16
17
|
const toError_1 = require("../../toError");
|
|
17
18
|
async function runExecutor(options, context) {
|
|
18
|
-
const graphPath = options.graphPath;
|
|
19
|
+
const graphPath = options.graphPath ?? graph_loader_1.DEFAULT_GRAPH_PATH;
|
|
19
20
|
const workspaceRoot = context.root;
|
|
21
|
+
const snapshot = new saved_snapshot_1.SavedSnapshot();
|
|
20
22
|
console.log('\n🎨 Architecture Visualization\n');
|
|
21
|
-
|
|
23
|
+
console.log(snapshot.message());
|
|
24
|
+
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- Nx command rendering boundary; success:false propagates failure to Nx
|
|
22
25
|
try {
|
|
23
26
|
// Load the saved graph
|
|
24
27
|
console.log('📂 Loading saved graph...');
|
|
25
|
-
const graphFile =
|
|
26
|
-
if (!graphFile) {
|
|
27
|
-
console.error('❌ No saved graph found at architecture/dependencies.json');
|
|
28
|
-
console.error(' Run: nx run architecture:generate first');
|
|
29
|
-
return { success: false };
|
|
30
|
-
}
|
|
28
|
+
const graphFile = snapshot.loadProjects(workspaceRoot, graphPath);
|
|
31
29
|
const graph = graphFile.projects;
|
|
32
30
|
// Generate visualization
|
|
33
31
|
console.log('🎨 Generating visualization...');
|
|
@@ -47,7 +45,7 @@ async function runExecutor(options, context) {
|
|
|
47
45
|
catch (err) {
|
|
48
46
|
const error = (0, toError_1.toError)(err);
|
|
49
47
|
const rendered = error instanceof rules_config_1.RuleFailError ? (0, rules_config_1.renderRuleFailForHuman)(error) : error.message;
|
|
50
|
-
console.error(
|
|
48
|
+
console.error(`❌ Visualization failed for ${graphPath}:`, rendered);
|
|
51
49
|
return { success: false };
|
|
52
50
|
}
|
|
53
51
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/visualize/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/visualize/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;AAiBH,8BAuCC;AArDD,6DAAyD;AACzD,yDAA4D;AAC5D,iEAA6D;AAC7D,0DAAgF;AAChF,2CAAwC;AAUzB,KAAK,UAAU,WAAW,CACrC,OAAiC,EACjC,OAAwB;IAExB,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,iCAAkB,CAAC;IAC1D,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IACnC,MAAM,QAAQ,GAAkB,IAAI,8BAAa,EAAE,CAAC;IAEpD,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IAEhC,uIAAuI;IACvI,IAAI,CAAC;QACD,uBAAuB;QACvB,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;QACzC,MAAM,SAAS,GAAG,QAAQ,CAAC,YAAY,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;QAClE,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,CAAC;QAEjC,yBAAyB;QACzB,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;QAC9C,MAAM,UAAU,GAAG,IAAI,kCAAe,EAAE,CAAC;QACzC,MAAM,QAAQ,GAAG,UAAU,CAAC,kBAAkB,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;QACrE,OAAO,CAAC,GAAG,CAAC,gBAAgB,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;QAEjD,yBAAyB;QACzB,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;QACxD,IAAI,UAAU,CAAC,iBAAiB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAClD,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACJ,OAAO,CAAC,GAAG,CAAC,2CAA2C,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;QAChF,CAAC;QAED,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,MAAM,QAAQ,GAAG,KAAK,YAAY,4BAAa,CAAC,CAAC,CAAC,IAAA,qCAAsB,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;QAChG,OAAO,CAAC,KAAK,CAAC,8BAA8B,SAAS,GAAG,EAAE,QAAQ,CAAC,CAAC;QACpE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;AACL,CAAC","sourcesContent":["/**\n * Visualize Executor\n *\n * Generates visual representations of the architecture graph (DOT + HTML)\n * and opens the visualization in a browser.\n *\n * Usage:\n * nx run architecture:visualize\n */\n\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 { 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\nexport default async function runExecutor(\n options: VisualizeExecutorOptions,\n context: ExecutorContext\n): Promise<ExecutorResult> {\n const graphPath = options.graphPath ?? DEFAULT_GRAPH_PATH;\n const workspaceRoot = context.root;\n const snapshot: SavedSnapshot = new SavedSnapshot();\n\n console.log('\\n🎨 Architecture Visualization\\n');\n console.log(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 // Load the saved graph\n console.log('📂 Loading saved graph...');\n const graphFile = snapshot.loadProjects(workspaceRoot, graphPath);\n const graph = graphFile.projects;\n\n // Generate visualization\n console.log('🎨 Generating visualization...');\n const visualizer = new GraphVisualizer();\n const vizPaths = visualizer.writeVisualization(graph, workspaceRoot);\n console.log(`✅ Generated: ${vizPaths.htmlPath}`);\n\n // Try to open in browser\n console.log('\\n🌐 Opening visualization in browser...');\n if (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"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/schema",
|
|
3
3
|
"title": "Visualize Executor",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "View the saved architecture snapshot without refreshing. Refresh: pnpm nx run architecture:generate.",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"properties": {
|
|
7
7
|
"graphPath": {
|
|
@@ -9,22 +9,20 @@
|
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
11
|
exports.default = runExecutor;
|
|
12
|
-
const
|
|
12
|
+
const saved_snapshot_1 = require("../../lib/saved-snapshot");
|
|
13
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
13
14
|
const runtime_visualizer_1 = require("../../lib/runtime-visualizer");
|
|
14
15
|
const runtime_config_1 = require("../../lib/runtime-config");
|
|
15
16
|
const graph_visualizer_1 = require("../../lib/graph-visualizer");
|
|
16
17
|
const toError_1 = require("../../toError");
|
|
17
18
|
async function runExecutor(_options, context) {
|
|
18
19
|
const workspaceRoot = context.root;
|
|
20
|
+
const snapshot = new saved_snapshot_1.SavedSnapshot();
|
|
19
21
|
console.log('\n🎨 Runtime Microservice Visualization\n');
|
|
20
|
-
|
|
22
|
+
console.log(snapshot.message());
|
|
23
|
+
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- Nx command rendering boundary; success:false propagates failure to Nx
|
|
21
24
|
try {
|
|
22
|
-
const graph =
|
|
23
|
-
if (!graph) {
|
|
24
|
-
console.error('❌ No architecture/runtime-dependencies.json found');
|
|
25
|
-
console.error(' Run: nx run architecture:generate first');
|
|
26
|
-
return { success: false };
|
|
27
|
-
}
|
|
25
|
+
const graph = snapshot.loadRuntime(workspaceRoot);
|
|
28
26
|
const config = (0, runtime_config_1.loadRuntimeConfig)(workspaceRoot);
|
|
29
27
|
const options = new runtime_visualizer_1.RuntimeVizOptions(config.showExternalNodes);
|
|
30
28
|
const vizPaths = (0, runtime_visualizer_1.writeRuntimeVisualization)(graph, workspaceRoot, undefined, options);
|
|
@@ -41,7 +39,8 @@ async function runExecutor(_options, context) {
|
|
|
41
39
|
}
|
|
42
40
|
catch (err) {
|
|
43
41
|
const error = (0, toError_1.toError)(err);
|
|
44
|
-
|
|
42
|
+
const rendered = error instanceof rules_config_1.RuleFailError ? (0, rules_config_1.renderRuleFailForHuman)(error) : error.message;
|
|
43
|
+
console.error('❌ Runtime visualization failed for architecture/runtime-dependencies.json:', rendered);
|
|
45
44
|
return { success: false };
|
|
46
45
|
}
|
|
47
46
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/visualize-runtime/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/visualize-runtime/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;AAkBH,8BAiCC;AAhDD,6DAAyD;AACzD,0DAAgF;AAChF,qEAA4F;AAC5F,6DAA6D;AAC7D,iEAA6D;AAC7D,2CAAwC;AAUzB,KAAK,UAAU,WAAW,CACrC,QAAiC,EACjC,OAAwB;IAExB,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IACnC,MAAM,QAAQ,GAAkB,IAAI,8BAAa,EAAE,CAAC;IAEpD,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IAEhC,uIAAuI;IACvI,IAAI,CAAC;QACD,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,IAAA,kCAAiB,EAAC,aAAa,CAAC,CAAC;QAChD,MAAM,OAAO,GAAG,IAAI,sCAAiB,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;QAChE,MAAM,QAAQ,GAAG,IAAA,8CAAyB,EAAC,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QACrF,OAAO,CAAC,GAAG,CAAC,gBAAgB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,gBAAgB,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;QAEjD,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;QACxD,IAAI,IAAI,kCAAe,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7D,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACJ,OAAO,CAAC,GAAG,CAAC,2CAA2C,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;QAChF,CAAC;QAED,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,MAAM,QAAQ,GAAG,KAAK,YAAY,4BAAa,CAAC,CAAC,CAAC,IAAA,qCAAsB,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;QAChG,OAAO,CAAC,KAAK,CAAC,4EAA4E,EAAE,QAAQ,CAAC,CAAC;QACtG,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;AACL,CAAC","sourcesContent":["/**\n * Visualize Runtime Executor\n *\n * Renders the runtime microservice graph (committed\n * architecture/runtime-dependencies.json) to DOT + HTML and opens it.\n *\n * Usage: nx run architecture:visualize-runtime\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { SavedSnapshot } from '../../lib/saved-snapshot';\nimport { RuleFailError, renderRuleFailForHuman } from '@webpieces/rules-config';\nimport { writeRuntimeVisualization, RuntimeVizOptions } from '../../lib/runtime-visualizer';\nimport { loadRuntimeConfig } from '../../lib/runtime-config';\nimport { GraphVisualizer } from '../../lib/graph-visualizer';\nimport { toError } from '../../toError';\n\nexport interface VisualizeRuntimeOptions {\n // No options.\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\nexport default async function runExecutor(\n _options: VisualizeRuntimeOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n const workspaceRoot = context.root;\n const snapshot: SavedSnapshot = new SavedSnapshot();\n\n console.log('\\n🎨 Runtime Microservice Visualization\\n');\n console.log(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 const graph = snapshot.loadRuntime(workspaceRoot);\n const config = loadRuntimeConfig(workspaceRoot);\n const options = new RuntimeVizOptions(config.showExternalNodes);\n const vizPaths = writeRuntimeVisualization(graph, workspaceRoot, undefined, options);\n console.log(`✅ Generated: ${vizPaths.dotPath}`);\n console.log(`✅ Generated: ${vizPaths.htmlPath}`);\n\n console.log('\\n🌐 Opening visualization in browser...');\n if (new GraphVisualizer().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('❌ Runtime visualization failed for architecture/runtime-dependencies.json:', rendered);\n return { success: false };\n }\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/schema",
|
|
3
3
|
"title": "Visualize Runtime Executor",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "View the saved runtime snapshot as DOT + HTML without refreshing. Refresh: pnpm nx run architecture:generate.",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"properties": {},
|
|
7
7
|
"required": []
|
|
@@ -18,12 +18,12 @@
|
|
|
18
18
|
* its own package owns its schema, and the generator's fail-closed components check covers it.
|
|
19
19
|
*/
|
|
20
20
|
import { DocumentedType } from '@webpieces/api-doc-model';
|
|
21
|
-
import {
|
|
21
|
+
import { WireSuffixEntry } from './wire-suffix-entry';
|
|
22
22
|
import { ProjectInfo } from '../project-info';
|
|
23
23
|
/** The `required-type-suffix` entries the wire closure enforces; empty when that rule is OFF. */
|
|
24
24
|
export declare class WireClosureRule {
|
|
25
|
-
readonly suffixEntries: readonly
|
|
26
|
-
constructor(suffixEntries: readonly
|
|
25
|
+
readonly suffixEntries: readonly WireSuffixEntry[];
|
|
26
|
+
constructor(suffixEntries: readonly WireSuffixEntry[]);
|
|
27
27
|
/** Only the role check — what a unit test or a repo with `required-type-suffix` OFF gets. */
|
|
28
28
|
static withoutSuffixes(): WireClosureRule;
|
|
29
29
|
/** `required-type-suffix`'s entries, unless that rule is OFF or time-boxed / branch-scoped off. */
|
|
@@ -22,8 +22,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
22
22
|
exports.WireClosure = exports.WireClosureVerdict = exports.WireClosureRule = void 0;
|
|
23
23
|
const tslib_1 = require("tslib");
|
|
24
24
|
const path = tslib_1.__importStar(require("path"));
|
|
25
|
-
const code_rules_1 = require("@webpieces/code-rules");
|
|
26
25
|
const rules_config_1 = require("@webpieces/rules-config");
|
|
26
|
+
const rules_config_2 = require("@webpieces/rules-config");
|
|
27
27
|
const role_resolver_1 = require("../role-resolver");
|
|
28
28
|
const rule_gate_1 = require("../rule-gate");
|
|
29
29
|
const API_LIB_ROLE = 'api-lib';
|
|
@@ -41,10 +41,10 @@ class WireClosureRule {
|
|
|
41
41
|
/** `required-type-suffix`'s entries, unless that rule is OFF or time-boxed / branch-scoped off. */
|
|
42
42
|
// webpieces-disable no-function-outside-class -- static factory of this class
|
|
43
43
|
static fromConfig(workspaceRoot) {
|
|
44
|
-
if (new rule_gate_1.RuleGate().skipReason(workspaceRoot,
|
|
44
|
+
if (new rule_gate_1.RuleGate().skipReason(workspaceRoot, 'required-type-suffix', true) !== null) {
|
|
45
45
|
return WireClosureRule.withoutSuffixes();
|
|
46
46
|
}
|
|
47
|
-
const rule = (0,
|
|
47
|
+
const rule = (0, rules_config_2.loadAndValidate)(workspaceRoot).resolved.rules.get('required-type-suffix');
|
|
48
48
|
const entries = rule?.options['entries'];
|
|
49
49
|
return new WireClosureRule(Array.isArray(entries) ? entries : []);
|
|
50
50
|
}
|
|
@@ -74,7 +74,7 @@ class WireClosure {
|
|
|
74
74
|
workspaceRoot;
|
|
75
75
|
rule;
|
|
76
76
|
roots;
|
|
77
|
-
picker = new
|
|
77
|
+
picker = new rules_config_1.FirstPathMatch();
|
|
78
78
|
constructor(workspaceRoot, projectInfos, rule) {
|
|
79
79
|
this.workspaceRoot = workspaceRoot;
|
|
80
80
|
this.rule = rule;
|
|
@@ -102,7 +102,7 @@ class WireClosure {
|
|
|
102
102
|
`string-literal union. Retagging '${owner.name}' is not a way out unless it holds only ` +
|
|
103
103
|
'contracts and wire types (validate-api-lib-tag refuses a role:api-lib with an implementation).'));
|
|
104
104
|
}
|
|
105
|
-
const governing = this.picker.winner(relFile, this.rule.suffixEntries);
|
|
105
|
+
const governing = this.picker.winner(relFile, this.rule.suffixEntries.map((entry) => new rules_config_1.PathMatchCandidate(entry.paths, entry)));
|
|
106
106
|
if (governing !== undefined && !this.endsInSuffix(type.name, governing.entry.suffixes)) {
|
|
107
107
|
verdicts.push(new WireClosureVerdict(`'${type.name}' goes over the wire from ${relFile} but does not end in one of ` +
|
|
108
108
|
`[${governing.entry.suffixes.join(', ')}], which the required-type-suffix entry ` +
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wire-closure.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/api-usage/wire-closure.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The WIRE CLOSURE of a contract (#1064, D4): every named type an `@ApiPath` contract reaches must be\n * DECLARED in a `role:api-lib` project, and must end in a suffix that project's `required-type-suffix`\n * entry demands.\n *\n * Why it lives here and not in the dependency rule: `api-lib-dependencies` only sees project EDGES. A\n * relative-path import into another library, or a DTO library that itself reaches outside, puts a type\n * on the wire without any edge the graph rule could refuse. The extractor already follows every type a\n * contract reaches through the compiler — the same walk `api-rules-for-openapi` / `api-rules-for-mcp`\n * run, with no generate tag needed — so this is one more question asked of each type that walk found.\n *\n * Every api-lib rule before this one (`required-type-suffix`, `one-enum-spelling-in-api-lib`,\n * `no-utility-types-in-api-lib`, `no-inline-import-in-api-lib`) reads only source files INSIDE the api\n * library. A type declared in another package was, by construction, outside every one of them — which is\n * how `AiProvider` and the `BrowserTelemetry` trio reached the wire from a general library unchecked.\n *\n * A type declared OUTSIDE the workspace (a published package under node_modules) is not judged here:\n * its own package owns its schema, and the generator's fail-closed components check covers it.\n */\n\nimport * as path from 'path';\nimport { DocumentedType } from '@webpieces/api-doc-model';\nimport { SuffixEntryPicker } from '@webpieces/code-rules';\nimport { loadAndValidate } from \"@webpieces/rules-config\";\nimport { RequiredTypeSuffixEntry } from \"@webpieces/code-rules\";\nimport { ProjectInfo } from '../project-info';\nimport { resolveRole } from '../role-resolver';\nimport { RuleGate } from '../rule-gate';\n\nconst API_LIB_ROLE = 'api-lib';\n\n/** The `required-type-suffix` entries the wire closure enforces; empty when that rule is OFF. */\nexport class WireClosureRule {\n constructor(public readonly suffixEntries: readonly RequiredTypeSuffixEntry[]) {}\n\n /** Only the role check — what a unit test or a repo with `required-type-suffix` OFF gets. */\n // webpieces-disable no-function-outside-class -- static factory of this class\n static withoutSuffixes(): WireClosureRule {\n return new WireClosureRule([]);\n }\n\n /** `required-type-suffix`'s entries, unless that rule is OFF or time-boxed / branch-scoped off. */\n // webpieces-disable no-function-outside-class -- static factory of this class\n static fromConfig(workspaceRoot: string): WireClosureRule {\n if (new RuleGate().skipReason(workspaceRoot, \"required-type-suffix\", true) !== null) {\n return WireClosureRule.withoutSuffixes();\n }\n const rule = loadAndValidate(workspaceRoot).resolved.rules.get(\"required-type-suffix\");\n const entries = rule?.options['entries'];\n return new WireClosureRule(Array.isArray(entries) ? (entries as RequiredTypeSuffixEntry[]) : []);\n }\n}\n\n/** ONE wire-closure finding: what is wrong with a reached type, and what to do. */\nexport class WireClosureVerdict {\n constructor(\n public readonly what: string,\n public readonly cure: string,\n ) {}\n}\n\n/** A workspace project root, absolute, for the longest-prefix owner lookup. */\nclass OwnedRoot {\n constructor(\n public readonly info: ProjectInfo,\n public readonly abs: string,\n ) {}\n}\n\n/** Judges every type a contract reaches against the api-lib role and the suffix rule. */\nexport class WireClosure {\n private readonly roots: OwnedRoot[];\n private readonly picker = new SuffixEntryPicker();\n\n constructor(\n private readonly workspaceRoot: string,\n projectInfos: Map<string, ProjectInfo>,\n private readonly rule: WireClosureRule,\n ) {\n this.roots = [...projectInfos.values()]\n .filter((info: ProjectInfo) => info.root !== '' && info.root !== '.')\n .map((info: ProjectInfo) => new OwnedRoot(info, path.resolve(workspaceRoot, info.root)))\n // Longest root first so a nested project wins over its parent.\n .sort((a: OwnedRoot, b: OwnedRoot) => b.abs.length - a.abs.length);\n }\n\n /** Every verdict for ONE reached type, declared in the file `absFile`. */\n judge(type: DocumentedType, absFile: string): WireClosureVerdict[] {\n const abs = path.resolve(this.workspaceRoot, absFile);\n if (abs.split(path.sep).includes('node_modules')) return [];\n const owner = this.ownerOf(abs);\n if (owner === null) return [];\n const relFile = path.relative(this.workspaceRoot, abs).split(path.sep).join('/');\n const role = resolveRole(owner).role ?? 'none';\n const declared = `${type.packageName ?? 'no package'} (project '${owner.name}', role:${role})`;\n const verdicts: WireClosureVerdict[] = [];\n if (role !== API_LIB_ROLE) {\n verdicts.push(new WireClosureVerdict(\n `'${type.name}' goes over the wire but is declared in ${declared}, which is not a role:api-lib ` +\n 'project — every type a contract reaches must be declared in an api library',\n `Move '${type.name}' into a role:api-lib project — as a '…Dto' string enum when it is a ` +\n `string-literal union. Retagging '${owner.name}' is not a way out unless it holds only ` +\n 'contracts and wire types (validate-api-lib-tag refuses a role:api-lib with an implementation).',\n ));\n }\n const governing = this.picker.winner(relFile, this.rule.suffixEntries);\n if (governing !== undefined && !this.endsInSuffix(type.name, governing.entry.suffixes)) {\n verdicts.push(new WireClosureVerdict(\n `'${type.name}' goes over the wire from ${relFile} but does not end in one of ` +\n `[${governing.entry.suffixes.join(', ')}], which the required-type-suffix entry ` +\n `'${governing.glob}' demands of it`,\n `Rename '${type.name}' to end in one of ${governing.entry.suffixes.join(', ')} — the suffix tells a ` +\n 'reader which layer a type belongs to, and a wire type is the layer a partner reads.',\n ));\n }\n return verdicts;\n }\n\n private endsInSuffix(name: string, suffixes: readonly string[]): boolean {\n return suffixes.some((suffix: string) => name.length > suffix.length && name.endsWith(suffix));\n }\n\n private ownerOf(abs: string): ProjectInfo | null {\n for (const root of this.roots) {\n if (abs === root.abs || abs.startsWith(root.abs + path.sep)) return root.info;\n }\n return null;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"wire-closure.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/api-usage/wire-closure.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The WIRE CLOSURE of a contract (#1064, D4): every named type an `@ApiPath` contract reaches must be\n * DECLARED in a `role:api-lib` project, and must end in a suffix that project's `required-type-suffix`\n * entry demands.\n *\n * Why it lives here and not in the dependency rule: `api-lib-dependencies` only sees project EDGES. A\n * relative-path import into another library, or a DTO library that itself reaches outside, puts a type\n * on the wire without any edge the graph rule could refuse. The extractor already follows every type a\n * contract reaches through the compiler — the same walk `api-rules-for-openapi` / `api-rules-for-mcp`\n * run, with no generate tag needed — so this is one more question asked of each type that walk found.\n *\n * Every api-lib rule before this one (`required-type-suffix`, `one-enum-spelling-in-api-lib`,\n * `no-utility-types-in-api-lib`, `no-inline-import-in-api-lib`) reads only source files INSIDE the api\n * library. A type declared in another package was, by construction, outside every one of them — which is\n * how `AiProvider` and the `BrowserTelemetry` trio reached the wire from a general library unchecked.\n *\n * A type declared OUTSIDE the workspace (a published package under node_modules) is not judged here:\n * its own package owns its schema, and the generator's fail-closed components check covers it.\n */\n\nimport * as path from 'path';\nimport { DocumentedType } from '@webpieces/api-doc-model';\nimport { FirstPathMatch, PathMatchCandidate } from '@webpieces/rules-config';\nimport { loadAndValidate } from '@webpieces/rules-config';\nimport { WireSuffixEntry } from './wire-suffix-entry';\nimport { ProjectInfo } from '../project-info';\nimport { resolveRole } from '../role-resolver';\nimport { RuleGate } from '../rule-gate';\n\nconst API_LIB_ROLE = 'api-lib';\n\n/** The `required-type-suffix` entries the wire closure enforces; empty when that rule is OFF. */\nexport class WireClosureRule {\n constructor(public readonly suffixEntries: readonly WireSuffixEntry[]) {}\n\n /** Only the role check — what a unit test or a repo with `required-type-suffix` OFF gets. */\n // webpieces-disable no-function-outside-class -- static factory of this class\n static withoutSuffixes(): WireClosureRule {\n return new WireClosureRule([]);\n }\n\n /** `required-type-suffix`'s entries, unless that rule is OFF or time-boxed / branch-scoped off. */\n // webpieces-disable no-function-outside-class -- static factory of this class\n static fromConfig(workspaceRoot: string): WireClosureRule {\n if (new RuleGate().skipReason(workspaceRoot, 'required-type-suffix', true) !== null) {\n return WireClosureRule.withoutSuffixes();\n }\n const rule = loadAndValidate(workspaceRoot).resolved.rules.get('required-type-suffix');\n const entries = rule?.options['entries'];\n return new WireClosureRule(Array.isArray(entries) ? (entries as WireSuffixEntry[]) : []);\n }\n}\n\n/** ONE wire-closure finding: what is wrong with a reached type, and what to do. */\nexport class WireClosureVerdict {\n constructor(\n public readonly what: string,\n public readonly cure: string,\n ) {}\n}\n\n/** A workspace project root, absolute, for the longest-prefix owner lookup. */\nclass OwnedRoot {\n constructor(\n public readonly info: ProjectInfo,\n public readonly abs: string,\n ) {}\n}\n\n/** Judges every type a contract reaches against the api-lib role and the suffix rule. */\nexport class WireClosure {\n private readonly roots: OwnedRoot[];\n private readonly picker = new FirstPathMatch();\n\n constructor(\n private readonly workspaceRoot: string,\n projectInfos: Map<string, ProjectInfo>,\n private readonly rule: WireClosureRule,\n ) {\n this.roots = [...projectInfos.values()]\n .filter((info: ProjectInfo) => info.root !== '' && info.root !== '.')\n .map((info: ProjectInfo) => new OwnedRoot(info, path.resolve(workspaceRoot, info.root)))\n // Longest root first so a nested project wins over its parent.\n .sort((a: OwnedRoot, b: OwnedRoot) => b.abs.length - a.abs.length);\n }\n\n /** Every verdict for ONE reached type, declared in the file `absFile`. */\n judge(type: DocumentedType, absFile: string): WireClosureVerdict[] {\n const abs = path.resolve(this.workspaceRoot, absFile);\n if (abs.split(path.sep).includes('node_modules')) return [];\n const owner = this.ownerOf(abs);\n if (owner === null) return [];\n const relFile = path.relative(this.workspaceRoot, abs).split(path.sep).join('/');\n const role = resolveRole(owner).role ?? 'none';\n const declared = `${type.packageName ?? 'no package'} (project '${owner.name}', role:${role})`;\n const verdicts: WireClosureVerdict[] = [];\n if (role !== API_LIB_ROLE) {\n verdicts.push(\n new WireClosureVerdict(\n `'${type.name}' goes over the wire but is declared in ${declared}, which is not a role:api-lib ` +\n 'project — every type a contract reaches must be declared in an api library',\n `Move '${type.name}' into a role:api-lib project — as a '…Dto' string enum when it is a ` +\n `string-literal union. Retagging '${owner.name}' is not a way out unless it holds only ` +\n 'contracts and wire types (validate-api-lib-tag refuses a role:api-lib with an implementation).',\n ),\n );\n }\n const governing = this.picker.winner(\n relFile,\n this.rule.suffixEntries.map(\n (entry: WireSuffixEntry) => new PathMatchCandidate(entry.paths, entry),\n ),\n );\n if (governing !== undefined && !this.endsInSuffix(type.name, governing.entry.suffixes)) {\n verdicts.push(\n new WireClosureVerdict(\n `'${type.name}' goes over the wire from ${relFile} but does not end in one of ` +\n `[${governing.entry.suffixes.join(', ')}], which the required-type-suffix entry ` +\n `'${governing.glob}' demands of it`,\n `Rename '${type.name}' to end in one of ${governing.entry.suffixes.join(', ')} — the suffix tells a ` +\n 'reader which layer a type belongs to, and a wire type is the layer a partner reads.',\n ),\n );\n }\n return verdicts;\n }\n\n private endsInSuffix(name: string, suffixes: readonly string[]): boolean {\n return suffixes.some(\n (suffix: string) => name.length > suffix.length && name.endsWith(suffix),\n );\n }\n\n private ownerOf(abs: string): ProjectInfo | null {\n for (const root of this.roots) {\n if (abs === root.abs || abs.startsWith(root.abs + path.sep)) return root.info;\n }\n return null;\n }\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WireSuffixEntry = void 0;
|
|
4
|
+
/** Structural settings consumed by this contributor; the declared canonical owner validates them. */
|
|
5
|
+
class WireSuffixEntry {
|
|
6
|
+
paths;
|
|
7
|
+
suffixes;
|
|
8
|
+
}
|
|
9
|
+
exports.WireSuffixEntry = WireSuffixEntry;
|
|
10
|
+
//# sourceMappingURL=wire-suffix-entry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wire-suffix-entry.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/api-usage/wire-suffix-entry.ts"],"names":[],"mappings":";;;AAAA,qGAAqG;AACrG,MAAa,eAAe;IACxB,KAAK,CAAY;IACjB,QAAQ,CAAY;CACvB;AAHD,0CAGC","sourcesContent":["/** Structural settings consumed by this contributor; the declared canonical owner validates them. */\nexport class WireSuffixEntry {\n paths!: string[];\n suffixes!: string[];\n}\n"]}
|
package/src/lib/graph-loader.js
CHANGED
|
@@ -67,9 +67,9 @@ exports.GRAPH_COMMANDS = {
|
|
|
67
67
|
'architecture/apis/<ApiName>.json and architecture/runtime-dependencies.json; run after ' +
|
|
68
68
|
'adding/removing project dependencies or changing an API\'s endpoints',
|
|
69
69
|
visualizeArchitecture: 'pnpm nx run architecture:visualize — opens the monorepo dependency graph (this file) ' +
|
|
70
|
-
'as HTML in a browser',
|
|
70
|
+
'as HTML in a browser from saved data, without refreshing; freshness unknown',
|
|
71
71
|
visualizeRuntimeArchitecture: 'pnpm nx run architecture:visualize-runtime — opens the runtime microservice call graph ' +
|
|
72
|
-
'as HTML in a browser',
|
|
72
|
+
'as HTML in a browser from saved data, without refreshing; freshness unknown',
|
|
73
73
|
regenerateDesigns: "pnpm nx run-many --target=di-graph-generate — rewrites every project's " +
|
|
74
74
|
'design.json/design.md (also runs automatically on every build)',
|
|
75
75
|
visualizeDesign: "pnpm wp-design-visualize <project> — opens a project's DI designs (its designFile) as " +
|