@webpieces/nx-webpieces-rules 0.4.847 → 0.4.849
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 +28 -15
- package/executors.json +1 -66
- package/package.json +9 -9
- 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/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/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-visualizer.d.ts +4 -4
- package/src/lib/runtime-visualizer.js +2 -2
- package/src/lib/runtime-visualizer.js.map +1 -1
- 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 +6 -40
- 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/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": []
|
|
@@ -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/rule-gate.d.ts
CHANGED
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
* semantics, from the same source of truth (`loadAndValidate` → webpieces.config.json).
|
|
10
10
|
*
|
|
11
11
|
* Semantics:
|
|
12
|
-
* -
|
|
13
|
-
* repo that has not adopted the keys yet, behaves exactly as it did before this gate existed.
|
|
12
|
+
* - absent policy → fail with an explicit declaration/config repair, never run on an inferred policy.
|
|
14
13
|
* - `"mode": "OFF"` → skip with a reason.
|
|
15
14
|
* - the time-box / branch escape hatches (turnOffRuleUntilEpoch / turnOffRuleWhileOnBranch) → honored
|
|
16
15
|
* when the caller passes `honorEpoch: true`. All five executors now pass true: a schedule ("do not
|
package/src/lib/rule-gate.js
CHANGED
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
* semantics, from the same source of truth (`loadAndValidate` → webpieces.config.json).
|
|
11
11
|
*
|
|
12
12
|
* Semantics:
|
|
13
|
-
* -
|
|
14
|
-
* repo that has not adopted the keys yet, behaves exactly as it did before this gate existed.
|
|
13
|
+
* - absent policy → fail with an explicit declaration/config repair, never run on an inferred policy.
|
|
15
14
|
* - `"mode": "OFF"` → skip with a reason.
|
|
16
15
|
* - the time-box / branch escape hatches (turnOffRuleUntilEpoch / turnOffRuleWhileOnBranch) → honored
|
|
17
16
|
* when the caller passes `honorEpoch: true`. All five executors now pass true: a schedule ("do not
|
|
@@ -22,6 +21,7 @@
|
|
|
22
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
22
|
exports.RuleGate = void 0;
|
|
24
23
|
const rules_config_1 = require("@webpieces/rules-config");
|
|
24
|
+
const rules_config_2 = require("@webpieces/rules-config");
|
|
25
25
|
class RuleGate {
|
|
26
26
|
/**
|
|
27
27
|
* @param workspaceRoot the Nx `context.root` — where webpieces.config.json is looked up from.
|
|
@@ -31,9 +31,8 @@ class RuleGate {
|
|
|
31
31
|
*/
|
|
32
32
|
skipReason(workspaceRoot, ruleName, honorEpoch) {
|
|
33
33
|
const rule = (0, rules_config_1.loadAndValidate)(workspaceRoot).resolved.rules.get(ruleName);
|
|
34
|
-
// Absent ⇒ run. Never invent a default that silently disables a check.
|
|
35
34
|
if (!rule)
|
|
36
|
-
|
|
35
|
+
throw new rules_config_2.RuleFailError('policy-configuration', `Policy ${ruleName} is not explicitly configured. Declare its owning pack and repair that pack's config file before running this check.`);
|
|
37
36
|
if (rule.isOff)
|
|
38
37
|
return 'mode: OFF';
|
|
39
38
|
if (!honorEpoch)
|
package/src/lib/rule-gate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-gate.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/rule-gate.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"rule-gate.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/rule-gate.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;GAkBG;;;AAEH,0DAA0E;AAC1E,0DAAwD;AAExD,MAAa,QAAQ;IACjB;;;;;OAKG;IACH,UAAU,CAAC,aAAqB,EAAE,QAAgB,EAAE,UAAmB;QACnE,MAAM,IAAI,GAAG,IAAA,8BAAe,EAAC,aAAa,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACzE,IAAI,CAAC,IAAI;YACL,MAAM,IAAI,4BAAa,CACnB,sBAAsB,EACtB,UAAU,QAAQ,sHAAsH,CAC3I,CAAC;QACN,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO,WAAW,CAAC;QACnC,IAAI,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC;QAE7B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,uBAAuB,CAAuB,CAAC;QAC1E,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,0BAA0B,CAA8B,CAAC;QACrF,MAAM,IAAI,GAAG,IAAA,6BAAc,EAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC3C,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,IAAI,sBAAsB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACtE,CAAC;IAED;;;OAGG;IACH,UAAU,CAAC,aAAqB,EAAE,QAAgB,EAAE,UAAmB;QACnE,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;QACpE,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC;QAClC,OAAO,CAAC,GAAG,CACP,kBAAkB,QAAQ,KAAK,MAAM,2CAA2C,CACnF,CAAC;QACF,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ;AAnCD,4BAmCC","sourcesContent":["/**\n * Config gate for the Nx infrastructure validators.\n *\n * Every other rule in the system (eslint rules, code-rules, ai-hook rules, validate-ts-in-src) can be\n * turned off or time-boxed from webpieces.config.json. The five infrastructure executors\n * (validate-architecture-unchanged, validate-no-architecture-cycles, validate-packagejson,\n * validate-versions-locked, validate-eslint-sync) hardcoded their behavior instead, which made them\n * the only checks a consuming repo could not adopt gradually. This gate gives them the same\n * semantics, from the same source of truth (`loadAndValidate` → webpieces.config.json).\n *\n * Semantics:\n * - absent policy → fail with an explicit declaration/config repair, never run on an inferred policy.\n * - `\"mode\": \"OFF\"` → skip with a reason.\n * - the time-box / branch escape hatches (turnOffRuleUntilEpoch / turnOffRuleWhileOnBranch) → honored\n * when the caller passes `honorEpoch: true`. All five executors now pass true: a schedule (\"do not\n * enforce until <epoch> / off <branch>\") is coherent for any of them, baseline or not (see the\n * comment block above ValidateArchitectureUnchangedConfig in rules-config/src/rule-configs.ts). The\n * param is retained so a future caller can still opt a rule out of time-boxing.\n */\n\nimport { loadAndValidate, shouldSkipRule } from '@webpieces/rules-config';\nimport { RuleFailError } from '@webpieces/rules-config';\n\nexport class RuleGate {\n /**\n * @param workspaceRoot the Nx `context.root` — where webpieces.config.json is looked up from.\n * @param ruleName the webpieces.config.json key, identical to the Nx target name.\n * @param honorEpoch true to honor the time-box/branch escape hatches (all five executors pass true).\n * @returns a human-readable reason when the executor must SKIP, or null when it must RUN.\n */\n skipReason(workspaceRoot: string, ruleName: string, honorEpoch: boolean): string | null {\n const rule = loadAndValidate(workspaceRoot).resolved.rules.get(ruleName);\n if (!rule)\n throw new RuleFailError(\n 'policy-configuration',\n `Policy ${ruleName} is not explicitly configured. Declare its owning pack and repair that pack's config file before running this check.`,\n );\n if (rule.isOff) return 'mode: OFF';\n if (!honorEpoch) return null;\n\n const epoch = rule.options['turnOffRuleUntilEpoch'] as number | undefined;\n const branch = rule.options['turnOffRuleWhileOnBranch'] as string | null | undefined;\n const skip = shouldSkipRule(epoch, branch);\n return skip.skip ? (skip.reason ?? 'temporarily disabled') : null;\n }\n\n /**\n * Convenience wrapper: prints the standard skip line and reports whether the caller should stop.\n * Keeps the four-line preamble identical across all five executors.\n */\n isDisabled(workspaceRoot: string, ruleName: string, honorEpoch: boolean): boolean {\n const reason = this.skipReason(workspaceRoot, ruleName, honorEpoch);\n if (reason === null) return false;\n console.log(\n `\\n⏭️ Skipping ${ruleName} (${reason}) — configured in webpieces.config.json\\n`,\n );\n return true;\n }\n}\n"]}
|
|
@@ -48,15 +48,15 @@ import type { RuntimeGraph } from './runtime-graph';
|
|
|
48
48
|
export declare class RuntimeVizOptions {
|
|
49
49
|
/**
|
|
50
50
|
* Draw the dashed terminal nodes for contracts nothing in-repo implements. On by default;
|
|
51
|
-
* a repo whose external surface is noisy can turn them off in
|
|
52
|
-
* (runtime-architecture.showExternalNodes).
|
|
51
|
+
* a repo whose external surface is noisy can turn them off in the declared Nx owner file's
|
|
52
|
+
* direct policy-ID map (runtime-architecture.showExternalNodes).
|
|
53
53
|
*/
|
|
54
54
|
readonly showExternalNodes: boolean;
|
|
55
55
|
constructor(
|
|
56
56
|
/**
|
|
57
57
|
* Draw the dashed terminal nodes for contracts nothing in-repo implements. On by default;
|
|
58
|
-
* a repo whose external surface is noisy can turn them off in
|
|
59
|
-
* (runtime-architecture.showExternalNodes).
|
|
58
|
+
* a repo whose external surface is noisy can turn them off in the declared Nx owner file's
|
|
59
|
+
* direct policy-ID map (runtime-architecture.showExternalNodes).
|
|
60
60
|
*/
|
|
61
61
|
showExternalNodes?: boolean);
|
|
62
62
|
}
|
|
@@ -64,8 +64,8 @@ class RuntimeVizOptions {
|
|
|
64
64
|
constructor(
|
|
65
65
|
/**
|
|
66
66
|
* Draw the dashed terminal nodes for contracts nothing in-repo implements. On by default;
|
|
67
|
-
* a repo whose external surface is noisy can turn them off in
|
|
68
|
-
* (runtime-architecture.showExternalNodes).
|
|
67
|
+
* a repo whose external surface is noisy can turn them off in the declared Nx owner file's
|
|
68
|
+
* direct policy-ID map (runtime-architecture.showExternalNodes).
|
|
69
69
|
*/
|
|
70
70
|
showExternalNodes = true) {
|
|
71
71
|
this.showExternalNodes = showExternalNodes;
|