@conformetry/validation 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jimmy Paolini
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,515 @@
1
+ # 👔 Conformetry Validation
2
+
3
+ The validation orchestrator for [Conformetry](../conformetry-cli/README.md):
4
+ it matches instances to templates, checks the declared files exist, routes
5
+ each document to the validator that claims its extension, and deduplicates
6
+ what comes back.
7
+
8
+ ```bash
9
+ npm install --save-dev @conformetry/validation
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ ```ts
15
+ import { ValidationService } from "@conformetry/validation";
16
+
17
+ const result = validationService.validate({
18
+ instances, // from DiscoveryService.findInstances
19
+ templates, // from DiscoveryService.collectTemplate
20
+ languageNames: ["typescript"], // optional filter
21
+ threshold: 0.9, // optional run-level conformance floor
22
+ });
23
+ // → { ok, fileResults, checkedPaths, scores, unmatched }
24
+ ```
25
+
26
+ Instances arrive from the caller. This package used to scan the workspace for
27
+ `project.json` files and infer scope from generator name suffixes, which made a
28
+ generic package depend on one repository's layout.
29
+
30
+ ## Order of checks
31
+
32
+ 1. **Files exist.** Every file the matched template declares is checked first,
33
+ whatever its extension. A missing file cannot be compared, and reporting it
34
+ once is clearer than every language reporting it in turn. See
35
+ [`@conformetry/languages`](../conformetry-languages/README.md), whose
36
+ `FilesService` owns that pass.
37
+ 2. **Documents compare.** Each validator sees only the documents whose
38
+ extensions it claims.
39
+
40
+ Instances that matched no template are reported alongside the content
41
+ differences rather than skipped, so one report covers both "this file is wrong"
42
+ and "conformetry cannot tell what this path was generated from".
43
+
44
+ ## Scoring
45
+
46
+ Each matched instance is scored by how much of its template it honours, and
47
+ passes when that reaches the threshold resolved for it — the instance group's,
48
+ else the generator's, else the run's, else a perfect `1`.
49
+
50
+ Scores are taken **before** deduplication. Deduplication decides which template
51
+ gets to _print_ a shared file's finding, which is a reporting concern; a score
52
+ answers how well one instance honours its own template, so collapsing the
53
+ report must not change it.
54
+
55
+ `ok` is `false` when any instance falls below its threshold, or when any
56
+ instance matched no template at all — an unmatched path has no template to be
57
+ held to, so no threshold could excuse it.
58
+
59
+ ## Language resolution
60
+
61
+ Which languages a run needs is
62
+ [`@conformetry/languages`](../conformetry-languages/README.md)'s question, not
63
+ this package's. `LanguagesService` is injected, asked which engines claim the
64
+ extensions the run's templates declare, and its answer is what `validate`
65
+ dispatches over. An extension no language claims is compared line by line by
66
+ the text fallback, so no template file goes unchecked.
67
+
68
+ Nothing is imported on demand any more. That mechanism bought install-time
69
+ optionality nobody was spending — the fallback makes every run need the
70
+ package regardless — so the specifier registry, the module loader, and the
71
+ `loadLanguageModule` option went with it. The trade-off, and the three
72
+ alternatives weighed against it, are recorded in
73
+ [ADR 0006](../../../../docs/adr/0006-hold-every-conformetry-language-in-one-package.md).
74
+
75
+ ## Exports
76
+
77
+ `ValidationService`, `ValidationDeduplicationService`,
78
+ `ValidationFindingsService`, `ValidationScoringService`, `ValidationModule`,
79
+ and the `RunValidationArguments`, `RunValidationResult`, and
80
+ `InstanceFileResults` types.
81
+
82
+ ## Test
83
+
84
+ ```bash
85
+ nx run conformetry-validation:vitest
86
+ ```
87
+
88
+ ## License
89
+
90
+ MIT — see [LICENSE](../../../../LICENSE).
91
+
92
+ ## 👔 Conformetry
93
+
94
+ This project was generated from the [nestjs-service-project](../../../../configuration/conformetry-templates/nestjs-service-project) conformetry template.
95
+
96
+ <!-- callidescope:start -->
97
+
98
+ ## 🔭 Callidescope
99
+
100
+ Call stacks traced through `packages/ic-suite/conformetry/conformetry-validation`, deepest first. Each frame shows what it takes, what it returns, and what its documentation says.
101
+
102
+ | Measure | Value |
103
+ | --- | --- |
104
+ | Callables | 43 |
105
+ | Files | 18 |
106
+ | Calls traced | 43 |
107
+ | Call stacks | 0 |
108
+ | Deepest stack | 0 |
109
+ | Stacks through recursion | 0 |
110
+ | Unfollowable calls | 1 |
111
+
112
+ ### Limits
113
+
114
+ What this project is judged against, as declared in its own `callidescope.config.ts`.
115
+
116
+ | Limit | Value |
117
+ | --- | --- |
118
+ | `maximumDepth` | 13 |
119
+ | `maximumBreadth` | 10 |
120
+
121
+ ### Call stacks (depth)
122
+
123
+ None.
124
+
125
+ ### Breadth
126
+
127
+ | Callable | Breadth | Calls directly | Location |
128
+ | --- | --- | --- | --- |
129
+ | `ValidationService.validate` | 10 | `ConfigurationService.matchInstances`, `ValidationService.selectValidators`, `LanguagesService.resolveValidators`, `ValidationService.readTemplateExtensions`, `ValidationService.map(…)`, `ValidationScoringService.scoreInstances`, `ValidationDeduplicationService.deduplicate`, `ValidationFindingsService.buildUnmatchedResults`, `ValidationService.map(…)`, `ValidationService.every(…)` | `packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation.service.ts:136` |
130
+ | `ValidationService.validateInstance` | 6 | `ConfigurationService.prepareDocuments`, `ValidationService.flatMap(…)`, `FilesService.checkInstanceFiles`, `ValidationService.map(…)`, `ValidationService.flatMap(…)`, `ValidationService.reduce(…)` | `packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation.service.ts:87` |
131
+ | `RunnerService.runValidator` | 4 | `RunnerService.map(…)`, `RunnerService.filter(…)`, `RunnerService.filter(…)`, `RunnerService.reduce(…)` | `packages/ic-suite/conformetry/conformetry-validation/src/modules/runner/runner.service.ts:81` |
132
+
133
+ <details>
134
+ <summary>17 more callables</summary>
135
+
136
+ | Callable | Breadth | Calls directly | Location |
137
+ | --- | --- | --- | --- |
138
+ | `ValidationScoringService.scoreInstance` | 4 | `ValidationScoringService.reduce(…)`, `ScoringService.calculateScore`, `ValidationScoringService.resolveThreshold`, `ValidationScoringService.resolveInstancePath` | `packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation-scoring.service.ts:74` |
139
+ | `ValidationDeduplicationService.deduplicate` | 2 | `ValidationDeduplicationService.selectOwners`, `ValidationDeduplicationService.flatMap(…)` | `packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation-deduplication.service.ts:90` |
140
+ | `ValidationFindingsService.buildUnmatchedResults` | 2 | `ValidationFindingsService.resolveTemplatesRootPath`, `ValidationFindingsService.map(…)` | `packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation-findings.service.ts:68` |
141
+ | `ValidationScoringService.scoreInstances` | 2 | `ValidationScoringService.scoreInstance`, `ValidationScoringService.resolveScoreKey` | `packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation-scoring.service.ts:111` |
142
+ | `RunnerService.filter(…)` | 1 | `RunnerService.claimsDocument` | `packages/ic-suite/conformetry/conformetry-validation/src/modules/runner/runner.service.ts:85` |
143
+ | `RunnerService.map(…)` | 1 | `RunnerService.validateDocument` | `packages/ic-suite/conformetry/conformetry-validation/src/modules/runner/runner.service.ts:88` |
144
+ | `ValidationDeduplicationService.selectOwners` | 1 | `ValidationDeduplicationService.compareInstances` | `packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation-deduplication.service.ts:57` |
145
+ | `ValidationDeduplicationService.flatMap(…)` | 1 | `ValidationDeduplicationService.filter(…)` | `packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation-deduplication.service.ts:94` |
146
+ | `ValidationDeduplicationService.filter(…)` | 1 | `ValidationDeduplicationService.resolveFindingKey` | `packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation-deduplication.service.ts:95` |
147
+ | `ValidationFindingsService.resolveTemplatesRootPath` | 1 | `ValidationFindingsService.map(…)` | `packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation-findings.service.ts:57` |
148
+ | `ValidationFindingsService.map(…)` | 1 | `ValidationFindingsService.describeReason` | `packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation-findings.service.ts:74` |
149
+ | `ValidationScoringService.reduce(…)` | 1 | `ScoringService.sumWeights` | `packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation-scoring.service.ts:75` |
150
+ | `ValidationService.readTemplateExtensions` | 1 | `ValidationService.flatMap(…)` | `packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation.service.ts:50` |
151
+ | `ValidationService.flatMap(…)` | 1 | `ValidationService.map(…)` | `packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation.service.ts:53` |
152
+ | `ValidationService.selectValidators` | 1 | `ValidationService.filter(…)` | `packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation.service.ts:63` |
153
+ | `ValidationService.map(…)` | 1 | `RunnerService.runValidator` | `packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation.service.ts:100` |
154
+ | `ValidationService.map(…)` | 1 | `ValidationService.validateInstance` | `packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation.service.ts:147` |
155
+
156
+ </details>
157
+ <!-- callidescope:end -->
158
+
159
+ ## 🕸️ Codependix
160
+
161
+ Dependency graphs exported by [codependix](https://github.com/Organizzolini/codebase/tree/main/packages/ic-suite/codependix/codependix-cli), regenerated by `nx run codebase:codependix:write`.
162
+
163
+ ### Nx Neighborhood
164
+
165
+ <!-- codependix:start name="codependix-nx-projects" -->
166
+ ```mermaid
167
+ graph LR
168
+ conformetry_cli["conformetry-cli"]
169
+ conformetry_configuration["conformetry-configuration"]
170
+ conformetry_core["conformetry-core"]
171
+ conformetry_examples["conformetry-examples"]
172
+ conformetry_languages["conformetry-languages"]
173
+ conformetry_nx["conformetry-nx"]
174
+ conformetry_validation["conformetry-validation"]
175
+ conformetry_cli --> conformetry_validation
176
+ conformetry_examples --> conformetry_validation
177
+ conformetry_nx --> conformetry_validation
178
+ conformetry_validation --> conformetry_configuration
179
+ conformetry_validation --> conformetry_core
180
+ conformetry_validation --> conformetry_languages
181
+ classDef subject fill:#7c3aed,color:#fff,stroke:#4c1d95,stroke-width:2px
182
+ class conformetry_validation subject
183
+ ```
184
+ <!-- codependix:end name="codependix-nx-projects" -->
185
+
186
+ ### NestJS Module Graph
187
+
188
+ <!-- codependix:start name="codependix-nestjs-modules" -->
189
+ ```mermaid
190
+ flowchart LR
191
+ ConfigurationModule
192
+ DifferencesModule
193
+ FilesModule
194
+ InputModule
195
+ InstanceDiscoveryModule
196
+ InstanceGroupModule
197
+ JsonModule
198
+ JupyterModule
199
+ LanguagesModule
200
+ MarkdownModule
201
+ PythonModule
202
+ RenderingModule
203
+ RunnerModule
204
+ ScoringModule
205
+ TemplateDiscoveryModule
206
+ TextModule
207
+ TypescriptModule
208
+ ValidationModule
209
+ ConfigurationModule --> InputModule
210
+ ConfigurationModule --> InstanceDiscoveryModule
211
+ ConfigurationModule --> InstanceGroupModule
212
+ ConfigurationModule --> RenderingModule
213
+ ConfigurationModule --> TemplateDiscoveryModule
214
+ FilesModule --> ConfigurationModule
215
+ FilesModule --> DifferencesModule
216
+ InstanceDiscoveryModule --> InstanceGroupModule
217
+ InstanceDiscoveryModule --> RenderingModule
218
+ InstanceDiscoveryModule --> TemplateDiscoveryModule
219
+ JsonModule --> ScoringModule
220
+ JupyterModule --> JsonModule
221
+ JupyterModule --> MarkdownModule
222
+ JupyterModule --> PythonModule
223
+ LanguagesModule --> JsonModule
224
+ LanguagesModule --> JupyterModule
225
+ LanguagesModule --> MarkdownModule
226
+ LanguagesModule --> PythonModule
227
+ LanguagesModule --> TextModule
228
+ LanguagesModule --> TypescriptModule
229
+ MarkdownModule --> ScoringModule
230
+ PythonModule --> DifferencesModule
231
+ PythonModule --> ScoringModule
232
+ TemplateDiscoveryModule --> RenderingModule
233
+ TypescriptModule --> ScoringModule
234
+ ValidationModule --> ConfigurationModule
235
+ ValidationModule --> FilesModule
236
+ ValidationModule --> LanguagesModule
237
+ ValidationModule --> RunnerModule
238
+ ValidationModule --> ScoringModule
239
+ ```
240
+ <!-- codependix:end name="codependix-nestjs-modules" -->
241
+
242
+ ### File Imports
243
+
244
+ <!-- codependix:start name="codependix-file-imports" -->
245
+ ```mermaid
246
+ graph LR
247
+ file_callidescope_config_ts["callidescope.config.ts"]
248
+ file_codependix_config_ts["codependix.config.ts"]
249
+ file_codometer_config_ts["codometer.config.ts"]
250
+ file_eslint_config_ts["eslint.config.ts"]
251
+ file_src_index_ts["src/index.ts"]
252
+ file_src_modules_runner_runner_constants_ts["src/modules/runner/runner.constants.ts"]
253
+ file_src_modules_runner_runner_module_ts["src/modules/runner/runner.module.ts"]
254
+ file_src_modules_runner_runner_module_unit_test_ts["src/modules/runner/runner.module.unit.test.ts"]
255
+ file_src_modules_runner_runner_service_ts["src/modules/runner/runner.service.ts"]
256
+ file_src_modules_runner_runner_service_unit_test_ts["src/modules/runner/runner.service.unit.test.ts"]
257
+ file_src_modules_runner_runner_types_ts["src/modules/runner/runner.types.ts"]
258
+ file_src_modules_validation_validation_deduplication_service_ts["src/modules/validation/validation-deduplication.service.ts"]
259
+ file_src_modules_validation_validation_deduplication_service_unit_test_ts["src/modules/validation/validation-deduplication.service.unit.test.ts"]
260
+ file_src_modules_validation_validation_findings_service_ts["src/modules/validation/validation-findings.service.ts"]
261
+ file_src_modules_validation_validation_findings_service_unit_test_ts["src/modules/validation/validation-findings.service.unit.test.ts"]
262
+ file_src_modules_validation_validation_scoring_service_ts["src/modules/validation/validation-scoring.service.ts"]
263
+ file_src_modules_validation_validation_scoring_service_unit_test_ts["src/modules/validation/validation-scoring.service.unit.test.ts"]
264
+ file_src_modules_validation_validation_constants_ts["src/modules/validation/validation.constants.ts"]
265
+ file_src_modules_validation_validation_module_ts["src/modules/validation/validation.module.ts"]
266
+ file_src_modules_validation_validation_service_ts["src/modules/validation/validation.service.ts"]
267
+ file_src_modules_validation_validation_service_unit_test_ts["src/modules/validation/validation.service.unit.test.ts"]
268
+ file_src_modules_validation_validation_types_ts["src/modules/validation/validation.types.ts"]
269
+ file_testing_mocks_ts["testing/mocks.ts"]
270
+ file_testing_setup_ts["testing/setup.ts"]
271
+ file_vite_config_ts["vite.config.ts"]
272
+ file_vitest_config_ts["vitest.config.ts"]
273
+ file_src_modules_runner_runner_module_ts --> file_src_modules_runner_runner_service_ts
274
+ file_src_modules_runner_runner_module_unit_test_ts --> file_src_modules_runner_runner_module_ts
275
+ file_src_modules_runner_runner_module_unit_test_ts --> file_src_modules_runner_runner_service_ts
276
+ file_src_modules_runner_runner_service_ts --> file_src_modules_runner_runner_types_ts
277
+ file_src_modules_runner_runner_service_unit_test_ts --> file_src_modules_runner_runner_service_ts
278
+ file_src_modules_validation_validation_deduplication_service_ts --> file_src_modules_validation_validation_constants_ts
279
+ file_src_modules_validation_validation_deduplication_service_ts --> file_src_modules_validation_validation_types_ts
280
+ file_src_modules_validation_validation_deduplication_service_unit_test_ts --> file_src_modules_validation_validation_deduplication_service_ts
281
+ file_src_modules_validation_validation_deduplication_service_unit_test_ts --> file_src_modules_validation_validation_types_ts
282
+ file_src_modules_validation_validation_findings_service_unit_test_ts --> file_src_modules_validation_validation_findings_service_ts
283
+ file_src_modules_validation_validation_scoring_service_ts --> file_src_modules_validation_validation_constants_ts
284
+ file_src_modules_validation_validation_scoring_service_ts --> file_src_modules_validation_validation_types_ts
285
+ file_src_modules_validation_validation_scoring_service_unit_test_ts --> file_src_modules_validation_validation_scoring_service_ts
286
+ file_src_modules_validation_validation_module_ts --> file_src_modules_runner_runner_module_ts
287
+ file_src_modules_validation_validation_module_ts --> file_src_modules_validation_validation_deduplication_service_ts
288
+ file_src_modules_validation_validation_module_ts --> file_src_modules_validation_validation_findings_service_ts
289
+ file_src_modules_validation_validation_module_ts --> file_src_modules_validation_validation_scoring_service_ts
290
+ file_src_modules_validation_validation_module_ts --> file_src_modules_validation_validation_service_ts
291
+ file_src_modules_validation_validation_service_ts --> file_src_modules_runner_runner_service_ts
292
+ file_src_modules_validation_validation_service_ts --> file_src_modules_validation_validation_deduplication_service_ts
293
+ file_src_modules_validation_validation_service_ts --> file_src_modules_validation_validation_findings_service_ts
294
+ file_src_modules_validation_validation_service_ts --> file_src_modules_validation_validation_scoring_service_ts
295
+ file_src_modules_validation_validation_service_ts --> file_src_modules_validation_validation_types_ts
296
+ file_src_modules_validation_validation_service_unit_test_ts --> file_src_modules_validation_validation_module_ts
297
+ file_src_modules_validation_validation_service_unit_test_ts --> file_src_modules_validation_validation_service_ts
298
+ ```
299
+ <!-- codependix:end name="codependix-file-imports" -->
300
+
301
+ <!-- codometer:start -->
302
+
303
+ ## ⏲️ Codometer
304
+
305
+ ### Project
306
+
307
+ ![Lines of Code](https://img.shields.io/badge/Lines_of_Code-2001-22c55e?style=flat-square)
308
+ ![Repository Size](https://img.shields.io/badge/Repository_Size-74.21_kB-6b7280?style=flat-square)
309
+ ![Folders](https://img.shields.io/badge/Folders-5-4a4a4a?style=flat-square)
310
+ ![Source Files](https://img.shields.io/badge/Source_Files-26-3178c6?style=flat-square)
311
+
312
+ ### Measured Targets
313
+
314
+ ![Compiled JavaScript Size](https://img.shields.io/badge/Compiled_JavaScript_Size-2.62_kB_gzip-6b7280?style=flat-square)
315
+
316
+ ### TypeScript
317
+
318
+ ![TypeScript Files](https://img.shields.io/badge/TypeScript_Files-26-3178c6?style=flat-square)
319
+ ![Interfaces](https://img.shields.io/badge/Interfaces-6-0ea5e9?style=flat-square)
320
+ ![Generic Declarations](https://img.shields.io/badge/Generic_Declarations-0-0369a1?style=flat-square)
321
+ ![Enums](https://img.shields.io/badge/Enums-0-f97316?style=flat-square)
322
+ ![Decorators](https://img.shields.io/badge/Decorators-7-db2777?style=flat-square)
323
+ ![Doc Comments](https://img.shields.io/badge/Doc_Comments-51-6366f1?style=flat-square)
324
+ ![Static Methods](https://img.shields.io/badge/Static_Methods-0-166534?style=flat-square)
325
+
326
+ ### JavaScript
327
+
328
+ ![JavaScript Files](https://img.shields.io/badge/JavaScript_Files-0-f7df1e?style=flat-square)
329
+ ![Test Files](https://img.shields.io/badge/Test_Files-6-10b981?style=flat-square)
330
+ ![External Packages](https://img.shields.io/badge/External_Packages-11-8b5cf6?style=flat-square)
331
+ ![Classes](https://img.shields.io/badge/Classes-7-7c3aed?style=flat-square)
332
+ ![Functions](https://img.shields.io/badge/Functions-89-16a34a?style=flat-square)
333
+ ![Methods](https://img.shields.io/badge/Methods-38-15803d?style=flat-square)
334
+ ![Sync Functions](https://img.shields.io/badge/Sync_Functions-112-4ade80?style=flat-square)
335
+ ![Async Functions](https://img.shields.io/badge/Async_Functions-15-059669?style=flat-square)
336
+ ![Constants](https://img.shields.io/badge/Constants-99-dc2626?style=flat-square)
337
+ ![Imports](https://img.shields.io/badge/Imports-89-0284c7?style=flat-square)
338
+ ![Exported Symbols](https://img.shields.io/badge/Exported_Symbols-18-ea580c?style=flat-square)
339
+ ![Comments](https://img.shields.io/badge/Comments-101-64748b?style=flat-square)
340
+ ![Comment Lines](https://img.shields.io/badge/Comment_Lines-267-475569?style=flat-square)
341
+ ![TODO Comments](https://img.shields.io/badge/TODO_Comments-0-ca8a04?style=flat-square)
342
+
343
+ ### Python
344
+
345
+ ![Python Files](https://img.shields.io/badge/Python_Files-0-3776ab?style=flat-square)
346
+ ![Python Lines](https://img.shields.io/badge/Python_Lines-0-4b8bbe?style=flat-square)
347
+ ![Python Classes](https://img.shields.io/badge/Python_Classes-0-7c3aed?style=flat-square)
348
+ ![Python Functions](https://img.shields.io/badge/Python_Functions-0-16a34a?style=flat-square)
349
+ ![Python Protocols](https://img.shields.io/badge/Python_Protocols-0-0ea5e9?style=flat-square)
350
+ ![Python Constants](https://img.shields.io/badge/Python_Constants-0-dc2626?style=flat-square)
351
+ ![Python Imports](https://img.shields.io/badge/Python_Imports-0-0284c7?style=flat-square)
352
+ ![Python Decorators](https://img.shields.io/badge/Python_Decorators-0-db2777?style=flat-square)
353
+ ![Docstrings](https://img.shields.io/badge/Docstrings-0-6366f1?style=flat-square)
354
+ ![Docstring Lines](https://img.shields.io/badge/Docstring_Lines-0-818cf8?style=flat-square)
355
+ ![Python Comments](https://img.shields.io/badge/Python_Comments-0-64748b?style=flat-square)
356
+ ![Python Comment Lines](https://img.shields.io/badge/Python_Comment_Lines-0-475569?style=flat-square)
357
+
358
+ ### JSON
359
+
360
+ ![JSON Files](https://img.shields.io/badge/JSON_Files-4-a16207?style=flat-square)
361
+ ![JSON Lines](https://img.shields.io/badge/JSON_Lines-164-ca8a04?style=flat-square)
362
+ ![JSON Objects](https://img.shields.io/badge/JSON_Objects-38-7c3aed?style=flat-square)
363
+ ![JSON Arrays](https://img.shields.io/badge/JSON_Arrays-13-8b5cf6?style=flat-square)
364
+ ![JSON Properties](https://img.shields.io/badge/JSON_Properties-102-0284c7?style=flat-square)
365
+ ![JSON Strings](https://img.shields.io/badge/JSON_Strings-89-16a34a?style=flat-square)
366
+ ![JSON Numbers](https://img.shields.io/badge/JSON_Numbers-1-059669?style=flat-square)
367
+ ![JSON Booleans](https://img.shields.io/badge/JSON_Booleans-7-0ea5e9?style=flat-square)
368
+ ![JSON Nulls](https://img.shields.io/badge/JSON_Nulls-0-64748b?style=flat-square)
369
+ ![JSON Items](https://img.shields.io/badge/JSON_Items-42-475569?style=flat-square)
370
+ ![JSON Nodes](https://img.shields.io/badge/JSON_Nodes-148-dc2626?style=flat-square)
371
+ ![JSON Max Depth](https://img.shields.io/badge/JSON_Max_Depth-5-ea580c?style=flat-square)
372
+
373
+ ### YAML
374
+
375
+ ![YAML Files](https://img.shields.io/badge/YAML_Files-0-cb171e?style=flat-square)
376
+ ![YAML Lines](https://img.shields.io/badge/YAML_Lines-0-e34c26?style=flat-square)
377
+ ![YAML Documents](https://img.shields.io/badge/YAML_Documents-0-f97316?style=flat-square)
378
+ ![YAML Mappings](https://img.shields.io/badge/YAML_Mappings-0-7c3aed?style=flat-square)
379
+ ![YAML Sequences](https://img.shields.io/badge/YAML_Sequences-0-8b5cf6?style=flat-square)
380
+ ![YAML Keys](https://img.shields.io/badge/YAML_Keys-0-0284c7?style=flat-square)
381
+ ![YAML Scalars](https://img.shields.io/badge/YAML_Scalars-0-16a34a?style=flat-square)
382
+ ![YAML Anchors](https://img.shields.io/badge/YAML_Anchors-0-059669?style=flat-square)
383
+ ![YAML Aliases](https://img.shields.io/badge/YAML_Aliases-0-10b981?style=flat-square)
384
+ ![YAML Comments](https://img.shields.io/badge/YAML_Comments-0-64748b?style=flat-square)
385
+ ![YAML Max Depth](https://img.shields.io/badge/YAML_Max_Depth-0-ea580c?style=flat-square)
386
+
387
+ ### TOML
388
+
389
+ ![TOML Files](https://img.shields.io/badge/TOML_Files-0-9c4221?style=flat-square)
390
+ ![TOML Lines](https://img.shields.io/badge/TOML_Lines-0-b45309?style=flat-square)
391
+ ![TOML Tables](https://img.shields.io/badge/TOML_Tables-0-7c3aed?style=flat-square)
392
+ ![TOML Array Tables](https://img.shields.io/badge/TOML_Array_Tables-0-8b5cf6?style=flat-square)
393
+ ![TOML Keys](https://img.shields.io/badge/TOML_Keys-0-0284c7?style=flat-square)
394
+ ![TOML Arrays](https://img.shields.io/badge/TOML_Arrays-0-16a34a?style=flat-square)
395
+ ![TOML Comments](https://img.shields.io/badge/TOML_Comments-0-64748b?style=flat-square)
396
+
397
+ ### Shell
398
+
399
+ ![Shell Files](https://img.shields.io/badge/Shell_Files-0-89e051?style=flat-square)
400
+ ![Shell Lines](https://img.shields.io/badge/Shell_Lines-0-4eaa25?style=flat-square)
401
+ ![Shell Functions](https://img.shields.io/badge/Shell_Functions-0-16a34a?style=flat-square)
402
+ ![Shell Variables](https://img.shields.io/badge/Shell_Variables-0-0284c7?style=flat-square)
403
+ ![Shell Exports](https://img.shields.io/badge/Shell_Exports-0-ea580c?style=flat-square)
404
+ ![Shell Conditionals](https://img.shields.io/badge/Shell_Conditionals-0-7c3aed?style=flat-square)
405
+ ![Shell Loops](https://img.shields.io/badge/Shell_Loops-0-8b5cf6?style=flat-square)
406
+ ![Shell Pipelines](https://img.shields.io/badge/Shell_Pipelines-0-059669?style=flat-square)
407
+ ![Shebangs](https://img.shields.io/badge/Shebangs-0-6b7280?style=flat-square)
408
+ ![Shell Comments](https://img.shields.io/badge/Shell_Comments-0-64748b?style=flat-square)
409
+ ![Shell Comment Lines](https://img.shields.io/badge/Shell_Comment_Lines-0-475569?style=flat-square)
410
+
411
+ ### SQL
412
+
413
+ ![SQL Files](https://img.shields.io/badge/SQL_Files-0-e38c00?style=flat-square)
414
+ ![SQL Lines](https://img.shields.io/badge/SQL_Lines-0-f29111?style=flat-square)
415
+ ![SQL Statements](https://img.shields.io/badge/SQL_Statements-0-7c3aed?style=flat-square)
416
+ ![SQL Selects](https://img.shields.io/badge/SQL_Selects-0-16a34a?style=flat-square)
417
+ ![SQL Inserts](https://img.shields.io/badge/SQL_Inserts-0-22c55e?style=flat-square)
418
+ ![SQL Updates](https://img.shields.io/badge/SQL_Updates-0-0ea5e9?style=flat-square)
419
+ ![SQL Deletes](https://img.shields.io/badge/SQL_Deletes-0-dc2626?style=flat-square)
420
+ ![SQL Creates](https://img.shields.io/badge/SQL_Creates-0-0284c7?style=flat-square)
421
+ ![SQL Joins](https://img.shields.io/badge/SQL_Joins-0-8b5cf6?style=flat-square)
422
+ ![SQL CTEs](https://img.shields.io/badge/SQL_CTEs-0-059669?style=flat-square)
423
+ ![SQL Comments](https://img.shields.io/badge/SQL_Comments-0-64748b?style=flat-square)
424
+
425
+ ### HCL
426
+
427
+ ![HCL Files](https://img.shields.io/badge/HCL_Files-0-844fba?style=flat-square)
428
+ ![HCL Lines](https://img.shields.io/badge/HCL_Lines-0-a78bfa?style=flat-square)
429
+ ![HCL Blocks](https://img.shields.io/badge/HCL_Blocks-0-7c3aed?style=flat-square)
430
+ ![HCL Resources](https://img.shields.io/badge/HCL_Resources-0-0284c7?style=flat-square)
431
+ ![HCL Variables](https://img.shields.io/badge/HCL_Variables-0-16a34a?style=flat-square)
432
+ ![HCL Outputs](https://img.shields.io/badge/HCL_Outputs-0-059669?style=flat-square)
433
+ ![HCL Attributes](https://img.shields.io/badge/HCL_Attributes-0-0ea5e9?style=flat-square)
434
+ ![HCL Interpolations](https://img.shields.io/badge/HCL_Interpolations-0-db2777?style=flat-square)
435
+ ![HCL Comments](https://img.shields.io/badge/HCL_Comments-0-64748b?style=flat-square)
436
+
437
+ ### CSS
438
+
439
+ ![CSS Files](https://img.shields.io/badge/CSS_Files-0-264de4?style=flat-square)
440
+ ![CSS Lines](https://img.shields.io/badge/CSS_Lines-0-2965f1?style=flat-square)
441
+ ![CSS Rules](https://img.shields.io/badge/CSS_Rules-0-7c3aed?style=flat-square)
442
+ ![CSS Selectors](https://img.shields.io/badge/CSS_Selectors-0-8b5cf6?style=flat-square)
443
+ ![CSS Declarations](https://img.shields.io/badge/CSS_Declarations-0-0284c7?style=flat-square)
444
+ ![CSS At Rules](https://img.shields.io/badge/CSS_At_Rules-0-f97316?style=flat-square)
445
+ ![CSS Media Queries](https://img.shields.io/badge/CSS_Media_Queries-0-ea580c?style=flat-square)
446
+ ![CSS Custom Properties](https://img.shields.io/badge/CSS_Custom_Properties-0-16a34a?style=flat-square)
447
+ ![CSS Comments](https://img.shields.io/badge/CSS_Comments-0-64748b?style=flat-square)
448
+
449
+ ### Conventions
450
+
451
+ ![Module Files](https://img.shields.io/badge/Module_Files-2-7c3aed?style=flat-square)
452
+ ![Service Files](https://img.shields.io/badge/Service_Files-5-0284c7?style=flat-square)
453
+ ![Command Files](https://img.shields.io/badge/Command_Files-0-16a34a?style=flat-square)
454
+ ![Constants Files](https://img.shields.io/badge/Constants_Files-2-ea580c?style=flat-square)
455
+ ![Types Files](https://img.shields.io/badge/Types_Files-2-db2777?style=flat-square)
456
+ ![Utilities Files](https://img.shields.io/badge/Utilities_Files-0-0ea5e9?style=flat-square)
457
+ ![TypeORM Entities](https://img.shields.io/badge/TypeORM_Entities-0-059669?style=flat-square)
458
+ ![Unit Tests](https://img.shields.io/badge/Unit_Tests-6-ca8a04?style=flat-square)
459
+ ![Integration Tests](https://img.shields.io/badge/Integration_Tests-0-7c3aed?style=flat-square)
460
+ ![End To End Tests](https://img.shields.io/badge/End_To_End_Tests-0-0284c7?style=flat-square)
461
+ ![CSS Comment Budget](https://img.shields.io/badge/CSS_Comment_Budget-0-16a34a?style=flat-square)
462
+ ![HCL Comment Budget](https://img.shields.io/badge/HCL_Comment_Budget-0-ea580c?style=flat-square)
463
+ ![Python Comment Budget](https://img.shields.io/badge/Python_Comment_Budget-0-db2777?style=flat-square)
464
+ ![SQL Comment Budget](https://img.shields.io/badge/SQL_Comment_Budget-0-0ea5e9?style=flat-square)
465
+ ![TOML Comment Budget](https://img.shields.io/badge/TOML_Comment_Budget-0-059669?style=flat-square)
466
+ ![TypeScript Comment Budget](https://img.shields.io/badge/TypeScript_Comment_Budget-0-ca8a04?style=flat-square)
467
+ ![YAML Comment Budget](https://img.shields.io/badge/YAML_Comment_Budget-0-7c3aed?style=flat-square)
468
+ ![Shell Comment Budget](https://img.shields.io/badge/Shell_Comment_Budget-0-0284c7?style=flat-square)
469
+
470
+ ### Jupyter
471
+
472
+ ![Notebooks](https://img.shields.io/badge/Notebooks-0-f37626?style=flat-square)
473
+ ![Notebook Cells](https://img.shields.io/badge/Notebook_Cells-0-e8a33d?style=flat-square)
474
+ ![Code Cells](https://img.shields.io/badge/Code_Cells-0-3776ab?style=flat-square)
475
+ ![Markdown Cells](https://img.shields.io/badge/Markdown_Cells-0-083fa1?style=flat-square)
476
+ ![Raw Cells](https://img.shields.io/badge/Raw_Cells-0-9ca3af?style=flat-square)
477
+ ![Executed Cells](https://img.shields.io/badge/Executed_Cells-0-16a34a?style=flat-square)
478
+ ![Cell Outputs](https://img.shields.io/badge/Cell_Outputs-0-059669?style=flat-square)
479
+ ![Notebook Code Lines](https://img.shields.io/badge/Notebook_Code_Lines-0-4b8bbe?style=flat-square)
480
+ ![Notebook Classes](https://img.shields.io/badge/Notebook_Classes-0-7c3aed?style=flat-square)
481
+ ![Notebook Functions](https://img.shields.io/badge/Notebook_Functions-0-22c55e?style=flat-square)
482
+ ![Notebook Imports](https://img.shields.io/badge/Notebook_Imports-0-0284c7?style=flat-square)
483
+ ![Notebook Decorators](https://img.shields.io/badge/Notebook_Decorators-0-db2777?style=flat-square)
484
+ ![Notebook Prose Lines](https://img.shields.io/badge/Notebook_Prose_Lines-0-1f6feb?style=flat-square)
485
+ ![Notebook Headings](https://img.shields.io/badge/Notebook_Headings-0-a78bfa?style=flat-square)
486
+ ![Notebook Links](https://img.shields.io/badge/Notebook_Links-0-10b981?style=flat-square)
487
+ ![Notebook Images](https://img.shields.io/badge/Notebook_Images-0-34d399?style=flat-square)
488
+ ![Notebook Code Blocks](https://img.shields.io/badge/Notebook_Code_Blocks-0-dc2626?style=flat-square)
489
+ ![Notebook Properties](https://img.shields.io/badge/Notebook_Properties-0-ca8a04?style=flat-square)
490
+ ![Notebook Nodes](https://img.shields.io/badge/Notebook_Nodes-0-a16207?style=flat-square)
491
+ ![Notebook Max Depth](https://img.shields.io/badge/Notebook_Max_Depth-0-ea580c?style=flat-square)
492
+
493
+ ### Markdown
494
+
495
+ ![Markdown Files](https://img.shields.io/badge/Markdown_Files-1-083fa1?style=flat-square)
496
+ ![Markdown Lines](https://img.shields.io/badge/Markdown_Lines-222-1f6feb?style=flat-square)
497
+ ![H1](https://img.shields.io/badge/H1-1-7c3aed?style=flat-square)
498
+ ![H2](https://img.shields.io/badge/H2-7-8b5cf6?style=flat-square)
499
+ ![H3](https://img.shields.io/badge/H3-12-a78bfa?style=flat-square)
500
+ ![H4](https://img.shields.io/badge/H4-0-c4b5fd?style=flat-square)
501
+ ![H5](https://img.shields.io/badge/H5-0-ddd6fe?style=flat-square)
502
+ ![H6](https://img.shields.io/badge/H6-0-ede9fe?style=flat-square)
503
+ ![Paragraphs](https://img.shields.io/badge/Paragraphs-45-64748b?style=flat-square)
504
+ ![Lists](https://img.shields.io/badge/Lists-6-16a34a?style=flat-square)
505
+ ![List Items](https://img.shields.io/badge/List_Items-25-22c55e?style=flat-square)
506
+ ![Task List Items](https://img.shields.io/badge/Task_List_Items-0-4ade80?style=flat-square)
507
+ ![Tables](https://img.shields.io/badge/Tables-2-0284c7?style=flat-square)
508
+ ![Table Rows](https://img.shields.io/badge/Table_Rows-10-0ea5e9?style=flat-square)
509
+ ![Links](https://img.shields.io/badge/Links-9-059669?style=flat-square)
510
+ ![Images](https://img.shields.io/badge/Images-0-10b981?style=flat-square)
511
+ ![Code Blocks](https://img.shields.io/badge/Code_Blocks-11-dc2626?style=flat-square)
512
+ ![Inline Code](https://img.shields.io/badge/Inline_Code-73-ef4444?style=flat-square)
513
+ ![Block Quotes](https://img.shields.io/badge/Block_Quotes-0-ca8a04?style=flat-square)
514
+ ![Thematic Breaks](https://img.shields.io/badge/Thematic_Breaks-0-a16207?style=flat-square)
515
+ <!-- codometer:end -->
@@ -0,0 +1,302 @@
1
+ import { ConfigurationService } from '@conformetry/configuration';
2
+ import { ConformetryLanguageValidator } from '@conformetry/core';
3
+ import { FilesService } from '@conformetry/languages';
4
+ import { Instance } from '@conformetry/configuration';
5
+ import { InstanceScore } from '@conformetry/core';
6
+ import { LanguagesService } from '@conformetry/languages';
7
+ import { LanguageValidatorResult } from '@conformetry/core';
8
+ import { MatchedInstance } from '@conformetry/configuration';
9
+ import { PreparedValidationDocument } from '@conformetry/core';
10
+ import { ScoringService } from '@conformetry/languages';
11
+ import { TemplateDefinition } from '@conformetry/configuration';
12
+ import { UnmatchedInstance } from '@conformetry/configuration';
13
+ import { ValidationFileResult } from '@conformetry/core';
14
+
15
+ /** Every finding one matched instance produced, kept with the instance. */
16
+ export declare interface InstanceFileResults {
17
+ readonly fileResults: ValidationFileResult[];
18
+ readonly instance: MatchedInstance;
19
+ /** Combined weight of everything this instance's template asked for. */
20
+ readonly totalWeight: number;
21
+ }
22
+
23
+ /** Arguments for running one language validator over a prepared document set. */
24
+ export declare interface RunLanguageValidatorArguments {
25
+ readonly checkedPaths: string[];
26
+ readonly documents: PreparedValidationDocument[];
27
+ readonly validator: ConformetryLanguageValidator;
28
+ }
29
+
30
+ /**
31
+ * Owns the shared envelope for running a language validator.
32
+ *
33
+ * Imported by `conformetry-validation`, the one consumer that drives the
34
+ * Languages. The Languages themselves implement the contract in
35
+ * `runner.types.ts` without importing this module — including the Jupyter
36
+ * Language, which composes its siblings directly rather than through the
37
+ * runner.
38
+ */
39
+ export declare class RunnerModule {
40
+ }
41
+
42
+ /**
43
+ * Runs a language validator over a prepared document set.
44
+ *
45
+ * This is the shared envelope every language package used to reimplement:
46
+ * select the documents this validator claims, compare each one, group the
47
+ * differences under their file, and assemble the result. Centralizing it means a
48
+ * language package contains only its comparison logic, and that every
49
+ * validator reports its outcome identically.
50
+ */
51
+ export declare class RunnerService {
52
+ constructor();
53
+ /**
54
+ * Returns whether a validator claims a document, by matching the instance
55
+ * file's extension against the validator descriptor.
56
+ */
57
+ private claimsDocument;
58
+ /** Compares one document and keeps its findings with its requirement size. */
59
+ private validateDocument;
60
+ /**
61
+ * Selects the documents a validator claims and returns its aggregated
62
+ * result.
63
+ *
64
+ * Documents the validator does not claim are skipped silently — several
65
+ * validators run over the same document set and each takes only its own
66
+ * extensions.
67
+ *
68
+ * Every claimed document contributes its weight, but only the failing ones
69
+ * are kept as file results. Reports should list faults, not files; scores
70
+ * need the whole denominator. Dropping clean documents before totalling
71
+ * would score an instance against only the parts of itself that are wrong,
72
+ * which reads as near-zero for an instance with one bad file among twenty.
73
+ */
74
+ runValidator(args: RunLanguageValidatorArguments): LanguageValidatorResult;
75
+ }
76
+
77
+ /** Arguments for one validation run. */
78
+ export declare interface RunValidationArguments {
79
+ /**
80
+ * The directories and files to validate. Callers expand their own globs —
81
+ * an Nx plugin filters by project tags, the CLI reads the config — so this
82
+ * package never needs to know what a workspace is.
83
+ */
84
+ readonly instances: Instance[];
85
+ /**
86
+ * Language names (`typescript`, `json`, …) to restrict the run to. Every
87
+ * language runs when this is absent or empty.
88
+ */
89
+ readonly languageNames?: string[];
90
+ /**
91
+ * The templates instances are matched against. Supplied rather than read
92
+ * from a root directory, because a template's location is a property of the
93
+ * generator that owns it.
94
+ */
95
+ readonly templates: TemplateDefinition[];
96
+ /**
97
+ * Lowest score any instance may have, unless its template or its instance
98
+ * group says otherwise. The weakest of the three levels.
99
+ */
100
+ readonly threshold?: number;
101
+ }
102
+
103
+ /** The outcome of one validation run. */
104
+ export declare interface RunValidationResult {
105
+ /** Every directory that was compared against a template. */
106
+ readonly checkedPaths: string[];
107
+ readonly fileResults: ValidationFileResult[];
108
+ readonly ok: boolean;
109
+ /** One score per matched instance, in the order they were matched. */
110
+ readonly scores: InstanceScore[];
111
+ /**
112
+ * Instances that matched no template, or matched two equally well. These
113
+ * are findings in their own right: a glob is the caller asserting the path
114
+ * is an instance, so matching nothing means the instance has drifted or the
115
+ * glob is wrong.
116
+ */
117
+ readonly unmatched: UnmatchedInstance[];
118
+ }
119
+
120
+ /** Arguments for scoring one matched instance. */
121
+ export declare interface ScoreInstanceArguments {
122
+ readonly fileResults: ValidationFileResult[];
123
+ readonly instance: MatchedInstance;
124
+ /** Run-level threshold, from a `--threshold` flag. The weakest level. */
125
+ readonly runThreshold?: number | undefined;
126
+ readonly totalWeight: number;
127
+ }
128
+
129
+ /** Arguments for scoring every matched instance of a run. */
130
+ export declare interface ScoreInstancesArguments {
131
+ readonly groups: InstanceFileResults[];
132
+ /** Run-level threshold, from a `--threshold` flag. The weakest level. */
133
+ readonly runThreshold?: number | undefined;
134
+ }
135
+
136
+ /**
137
+ * Collapses the findings of overlapping instances into one report.
138
+ *
139
+ * One file is usually covered by several instances — `logger.service.ts` is
140
+ * reached by the project glob, the module glob, and the service-file glob —
141
+ * and each of those matches a different template. Reporting all three would
142
+ * print the same defect three times, phrased against templates of wildly
143
+ * different size.
144
+ */
145
+ export declare class ValidationDeduplicationService {
146
+ constructor();
147
+ /**
148
+ * Orders two instances by template size, then template name.
149
+ *
150
+ * Smallest wins, because the smallest template that still explains the file
151
+ * is the narrowest thing the reader has to fix: `nestjs-service-file` says
152
+ * "this service file is wrong", `nestjs-service-project` says "this project
153
+ * of eighteen files is wrong" about the same line.
154
+ */
155
+ private compareInstances;
156
+ /** Keys a finding by the instance and template file pair it came from. */
157
+ private resolveFindingKey;
158
+ /** Picks, for each instance file, the group whose template is smallest. */
159
+ private selectOwners;
160
+ /**
161
+ * Returns one finding per instance file, taken from the smallest template
162
+ * that reported it, with duplicate instance/template pairs collapsed.
163
+ *
164
+ * Known property, not a bug: a file is reported against exactly one
165
+ * template, so a requirement unique to a larger template goes unreported for
166
+ * files a smaller template also covers. That is acceptable while the
167
+ * overlapping templates share their file contents, and is the price of not
168
+ * printing the same defect three times.
169
+ */
170
+ deduplicate(groups: InstanceFileResults[]): ValidationFileResult[];
171
+ }
172
+
173
+ /**
174
+ * Turns instances that matched no template into ordinary findings.
175
+ *
176
+ * Silence would be the wrong answer here. A glob is the author asserting that
177
+ * a path holds generated code, so a path matching nothing means either the
178
+ * instance drifted past recognition or the glob is wrong — both worth saying
179
+ * out loud, and both invisible if unmatched instances were merely skipped.
180
+ */
181
+ export declare class ValidationFindingsService {
182
+ constructor();
183
+ /** Describes why an instance could not be attributed to one template. */
184
+ private describeReason;
185
+ /**
186
+ * The directory the templates share, used as the "template" line of an
187
+ * unmatched finding — there is no single template to point at, and the set
188
+ * they were drawn from is the most useful thing to name.
189
+ */
190
+ private resolveTemplatesRootPath;
191
+ /** Builds one finding per unmatched instance, in instance order. */
192
+ buildUnmatchedResults(args: {
193
+ templates: TemplateDefinition[];
194
+ unmatched: UnmatchedInstance[];
195
+ }): ValidationFileResult[];
196
+ }
197
+
198
+ /**
199
+ * Orchestrates a validation run across the languages it resolves.
200
+ *
201
+ * `LanguagesModule` is imported rather than the languages being handed in:
202
+ * every run needs the Fallback, so there was never a run that carried fewer
203
+ * than all of them.
204
+ */
205
+ export declare class ValidationModule {
206
+ }
207
+
208
+ /**
209
+ * Scores each matched instance and decides whether it clears its threshold.
210
+ *
211
+ * Scoring is deliberately separate from the report. Deduplication picks which
212
+ * template gets to *print* a shared file's finding, but a score answers a
213
+ * different question — how much of this instance's own template it honours —
214
+ * so it is taken from what validation found before any of that is collapsed.
215
+ */
216
+ export declare class ValidationScoringService {
217
+ private readonly scoringService;
218
+ constructor(scoringService: ScoringService);
219
+ /**
220
+ * The instance's own directory.
221
+ *
222
+ * `Instance.path` is the directory the template's tree is laid *over* — the
223
+ * parent — so every module in a project shares it. Joining the name stem is
224
+ * what tells `aspects` from `ephemeris`, and without it every module of a
225
+ * project collapses onto one score.
226
+ */
227
+ private resolveInstancePath;
228
+ /** Keys a score by the instance and template it describes. */
229
+ private resolveScoreKey;
230
+ /**
231
+ * Resolves which threshold applies, narrowest level first.
232
+ *
233
+ * An instance group is more specific than the generator that owns it, and
234
+ * the generator is more specific than a flag covering the whole run. The
235
+ * built-in default applies only when nothing else has an opinion, which is
236
+ * why no earlier level may be pre-filled with it.
237
+ */
238
+ resolveThreshold(args: ScoreInstanceArguments): number;
239
+ /** Scores one matched instance against the threshold that applies to it. */
240
+ scoreInstance(args: ScoreInstanceArguments): InstanceScore;
241
+ /**
242
+ * Scores every matched instance, reporting each instance and template pair
243
+ * once.
244
+ *
245
+ * The same directory is matched repeatedly when several generators declare
246
+ * overlapping globs — `src/modules/*` belongs to more than one template — so
247
+ * without this the summary prints the same score several times over. A
248
+ * genuine tie against two *different* templates is kept: those are two real
249
+ * requirements the instance answers to.
250
+ *
251
+ * When two groups claim the same instance with different thresholds, the
252
+ * strictest wins. Nothing makes one group more specific than another, so
253
+ * order would otherwise decide it; letting the strictest win at least means
254
+ * adding a lenient group can never silently relax a bar someone else set.
255
+ */
256
+ scoreInstances(args: ScoreInstancesArguments): InstanceScore[];
257
+ }
258
+
259
+ /**
260
+ * Runs a full conformetry validation: match instances to templates, check the
261
+ * files exist, then compare contents language by language.
262
+ *
263
+ * The instances arrive from the caller. This package used to scan the
264
+ * workspace for `project.json` files and infer scope from generator name
265
+ * suffixes, which made a generic package depend on one repository's layout.
266
+ */
267
+ export declare class ValidationService {
268
+ private readonly configurationService;
269
+ private readonly filesService;
270
+ private readonly languagesService;
271
+ private readonly runnerService;
272
+ private readonly validationDeduplicationService;
273
+ private readonly validationFindingsService;
274
+ private readonly validationScoringService;
275
+ constructor(configurationService: ConfigurationService, filesService: FilesService, languagesService: LanguagesService, runnerService: RunnerService, validationDeduplicationService: ValidationDeduplicationService, validationFindingsService: ValidationFindingsService, validationScoringService: ValidationScoringService);
276
+ /** Every distinct file extension the matched templates declare. */
277
+ private readTemplateExtensions;
278
+ /** Narrows the resolved validators to the names the caller asked for. */
279
+ private selectValidators;
280
+ /**
281
+ * Checks one instance's files exist, then compares the documents whose
282
+ * extensions the selected validators claim.
283
+ *
284
+ * File existence comes first because a missing file cannot be compared, and
285
+ * reporting it once is clearer than every language reporting it in turn.
286
+ */
287
+ private validateInstance;
288
+ /**
289
+ * Validates every instance and returns the differences found.
290
+ *
291
+ * Instances that matched no template are reported alongside the content
292
+ * differences rather than skipped, so one report covers both "this file is
293
+ * wrong" and "conformetry cannot tell what this path was generated from".
294
+ *
295
+ * Synchronous, because resolving the languages was the only step that ever
296
+ * waited on anything: they were imported on demand, and now they are
297
+ * injected.
298
+ */
299
+ validate(args: RunValidationArguments): RunValidationResult;
300
+ }
301
+
302
+ export { }
@@ -0,0 +1,278 @@
1
+ import { Injectable as e, Module as t } from "@nestjs/common";
2
+ import n from "node:path";
3
+ import { FilesModule as r, FilesService as i, LanguagesModule as a, LanguagesService as o, ScoringModule as s, ScoringService as c } from "@conformetry/languages";
4
+ import { ConfigurationModule as l, ConfigurationService as u } from "@conformetry/configuration";
5
+ //#region \0@oxc-project+runtime@0.151.0/helpers/esm/decorateMetadata.js
6
+ function d(e, t) {
7
+ if (typeof Reflect == "object" && typeof Reflect.metadata == "function") return Reflect.metadata(e, t);
8
+ }
9
+ //#endregion
10
+ //#region \0@oxc-project+runtime@0.151.0/helpers/esm/decorate.js
11
+ function f(e, t, n, r) {
12
+ var i = arguments.length, a = i < 3 ? t : r === null ? r = Object.getOwnPropertyDescriptor(t, n) : r, o;
13
+ if (typeof Reflect == "object" && typeof Reflect.decorate == "function") a = Reflect.decorate(e, t, n, r);
14
+ else for (var s = e.length - 1; s >= 0; s--) (o = e[s]) && (a = (i < 3 ? o(a) : i > 3 ? o(t, n, a) : o(t, n)) || a);
15
+ return i > 3 && a && Object.defineProperty(t, n, a), a;
16
+ }
17
+ //#endregion
18
+ //#region packages/ic-suite/conformetry/conformetry-validation/src/modules/runner/runner.service.ts
19
+ var p = class {
20
+ constructor() {}
21
+ claimsDocument(e) {
22
+ let t = n.extname(e.document.filename);
23
+ return e.validator.descriptor.fileExtensions.includes(t);
24
+ }
25
+ validateDocument(e) {
26
+ let { differences: t, totalWeight: n } = e.validator.validateDocument(e.document);
27
+ return {
28
+ differences: t,
29
+ filename: e.document.filename,
30
+ instanceFilePath: e.document.instanceFilePath,
31
+ templateFilePath: e.document.templateFilePath,
32
+ totalWeight: n
33
+ };
34
+ }
35
+ runValidator(e) {
36
+ let t = e.documents.filter((t) => this.claimsDocument({
37
+ document: t,
38
+ validator: e.validator
39
+ })).map((t) => this.validateDocument({
40
+ document: t,
41
+ validator: e.validator
42
+ })), n = t.filter((e) => e.differences.length > 0);
43
+ return {
44
+ checkedPaths: e.checkedPaths,
45
+ fileResults: n,
46
+ languageName: e.validator.descriptor.name,
47
+ ok: n.length === 0,
48
+ totalWeight: t.reduce((e, t) => e + t.totalWeight, 0)
49
+ };
50
+ }
51
+ };
52
+ p = f([e(), d("design:paramtypes", [])], p);
53
+ //#endregion
54
+ //#region packages/ic-suite/conformetry/conformetry-validation/src/modules/runner/runner.module.ts
55
+ var m = class {};
56
+ m = f([t({
57
+ controllers: [],
58
+ exports: [p],
59
+ imports: [],
60
+ providers: [p]
61
+ })], m);
62
+ //#endregion
63
+ //#region packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation-deduplication.service.ts
64
+ var h = class {
65
+ constructor() {}
66
+ compareInstances(e, t) {
67
+ return e.template.filePaths.length === t.template.filePaths.length ? e.template.name.localeCompare(t.template.name) : e.template.filePaths.length - t.template.filePaths.length;
68
+ }
69
+ resolveFindingKey(e) {
70
+ return [e.instanceFilePath, e.templateFilePath].join("\0");
71
+ }
72
+ selectOwners(e) {
73
+ let t = /* @__PURE__ */ new Map();
74
+ for (let n of e) for (let e of n.fileResults) {
75
+ let r = t.get(e.instanceFilePath);
76
+ (r === void 0 || this.compareInstances(n.instance, r) < 0) && t.set(e.instanceFilePath, n.instance);
77
+ }
78
+ return t;
79
+ }
80
+ deduplicate(e) {
81
+ let t = this.selectOwners(e), n = /* @__PURE__ */ new Set();
82
+ return e.flatMap((e) => e.fileResults.filter((r) => {
83
+ if (t.get(r.instanceFilePath) !== e.instance) return !1;
84
+ let i = this.resolveFindingKey(r);
85
+ return !n.has(i) && (n.add(i), !0);
86
+ }));
87
+ }
88
+ };
89
+ h = f([e(), d("design:paramtypes", [])], h);
90
+ //#endregion
91
+ //#region packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation-findings.service.ts
92
+ var g = class {
93
+ constructor() {}
94
+ describeReason(e) {
95
+ if (e.reason === "ambiguous") {
96
+ let t = e.tiedTemplateNames.join(", ");
97
+ return {
98
+ fix: `Give the templates ${t} distinguishing files, or narrow the instance glob so only one applies.`,
99
+ message: `Ambiguous instance: matches ${t} equally well`
100
+ };
101
+ }
102
+ return {
103
+ fix: "Regenerate the instance from its template, or remove it from the instance globs if it is not generated code.",
104
+ message: "Unmatched instance: no template explains this path"
105
+ };
106
+ }
107
+ resolveTemplatesRootPath(e) {
108
+ return e.map((e) => n.dirname(e.directoryPath)).toSorted()[0] ?? "";
109
+ }
110
+ buildUnmatchedResults(e) {
111
+ let t = this.resolveTemplatesRootPath(e.templates);
112
+ return e.unmatched.map((e) => {
113
+ let r = n.join(e.instance.path, e.instance.nameStem);
114
+ return {
115
+ differences: [{
116
+ differenceType: "instance",
117
+ ...this.describeReason({
118
+ reason: e.reason,
119
+ tiedTemplateNames: e.tiedTemplateNames
120
+ })
121
+ }],
122
+ filename: e.instance.nameStem,
123
+ instanceFilePath: r,
124
+ templateFilePath: t,
125
+ totalWeight: 0
126
+ };
127
+ });
128
+ }
129
+ };
130
+ g = f([e(), d("design:paramtypes", [])], g);
131
+ //#endregion
132
+ //#region packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation-scoring.service.ts
133
+ var _ = class {
134
+ scoringService;
135
+ constructor(e) {
136
+ this.scoringService = e;
137
+ }
138
+ resolveInstancePath(e) {
139
+ return n.join(e.instance.path, e.instance.nameStem);
140
+ }
141
+ resolveScoreKey(e) {
142
+ return [e.instancePath, e.templateName].join("\0");
143
+ }
144
+ resolveThreshold(e) {
145
+ return e.instance.instance.threshold ?? e.instance.template.threshold ?? e.runThreshold ?? 1;
146
+ }
147
+ scoreInstance(e) {
148
+ let t = e.fileResults.reduce((e, t) => e + this.scoringService.sumWeights(t.differences), 0), n = e.totalWeight, r = this.scoringService.calculateScore({
149
+ failedWeight: t,
150
+ totalWeight: n
151
+ }), i = this.resolveThreshold(e);
152
+ return {
153
+ failedWeight: t,
154
+ instancePath: this.resolveInstancePath(e.instance),
155
+ ok: r >= i,
156
+ score: r,
157
+ templateName: e.instance.template.name,
158
+ threshold: i,
159
+ totalWeight: n
160
+ };
161
+ }
162
+ scoreInstances(e) {
163
+ let t = /* @__PURE__ */ new Map();
164
+ for (let n of e.groups) {
165
+ let r = this.scoreInstance({
166
+ fileResults: n.fileResults,
167
+ instance: n.instance,
168
+ runThreshold: e.runThreshold,
169
+ totalWeight: n.totalWeight
170
+ }), i = this.resolveScoreKey(r), a = t.get(i);
171
+ (a === void 0 || r.threshold > a.threshold) && t.set(i, r);
172
+ }
173
+ return [...t.values()];
174
+ }
175
+ };
176
+ _ = f([e(), d("design:paramtypes", [c === void 0 ? Object : c])], _);
177
+ //#endregion
178
+ //#region packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation.service.ts
179
+ var v = class {
180
+ configurationService;
181
+ filesService;
182
+ languagesService;
183
+ runnerService;
184
+ validationDeduplicationService;
185
+ validationFindingsService;
186
+ validationScoringService;
187
+ constructor(e, t, n, r, i, a, o) {
188
+ this.configurationService = e, this.filesService = t, this.languagesService = n, this.runnerService = r, this.validationDeduplicationService = i, this.validationFindingsService = a, this.validationScoringService = o;
189
+ }
190
+ readTemplateExtensions(e) {
191
+ return [...new Set(e.flatMap((e) => e.template.filePaths.map((e) => n.extname(e))))];
192
+ }
193
+ selectValidators(e) {
194
+ let { languageNames: t } = e;
195
+ return t === void 0 || t.length === 0 ? e.validators : e.validators.filter((e) => t.includes(e.descriptor.name));
196
+ }
197
+ validateInstance(e) {
198
+ let [t] = this.configurationService.prepareDocuments({
199
+ fileExtensions: e.validators.flatMap((e) => [...e.descriptor.fileExtensions]),
200
+ instances: [e.instance]
201
+ }), n = this.filesService.checkInstanceFiles({ instances: [e.instance] }), r = e.validators.map((n) => this.runnerService.runValidator({
202
+ checkedPaths: [e.instance.instance.path],
203
+ documents: t?.documents ?? [],
204
+ validator: n
205
+ }));
206
+ return {
207
+ fileResults: [...n.fileResults, ...r.flatMap((e) => e.fileResults)],
208
+ instance: e.instance,
209
+ totalWeight: r.reduce((e, t) => e + t.totalWeight, n.totalWeight)
210
+ };
211
+ }
212
+ validate(e) {
213
+ let { matched: t, unmatched: n } = this.configurationService.matchInstances({
214
+ instances: e.instances,
215
+ templates: e.templates
216
+ }), r = this.selectValidators({
217
+ languageNames: e.languageNames,
218
+ validators: this.languagesService.resolveValidators({ extensions: this.readTemplateExtensions(t) })
219
+ }), i = t.map((e) => this.validateInstance({
220
+ instance: e,
221
+ validators: r
222
+ })), a = this.validationScoringService.scoreInstances({
223
+ groups: i,
224
+ runThreshold: e.threshold
225
+ }), o = [...this.validationDeduplicationService.deduplicate(i), ...this.validationFindingsService.buildUnmatchedResults({
226
+ templates: e.templates,
227
+ unmatched: n
228
+ })];
229
+ return {
230
+ checkedPaths: t.map((e) => e.instance.path),
231
+ fileResults: o,
232
+ ok: n.length === 0 && a.every((e) => e.ok),
233
+ scores: a,
234
+ unmatched: n
235
+ };
236
+ }
237
+ };
238
+ v = f([e(), d("design:paramtypes", [
239
+ u === void 0 ? Object : u,
240
+ i === void 0 ? Object : i,
241
+ o === void 0 ? Object : o,
242
+ p === void 0 ? Object : p,
243
+ h === void 0 ? Object : h,
244
+ g === void 0 ? Object : g,
245
+ _ === void 0 ? Object : _
246
+ ])], v);
247
+ //#endregion
248
+ //#region packages/ic-suite/conformetry/conformetry-validation/src/modules/validation/validation.module.ts
249
+ var y = class {};
250
+ y = f([t({
251
+ controllers: [],
252
+ exports: [
253
+ l,
254
+ r,
255
+ a,
256
+ m,
257
+ s,
258
+ h,
259
+ g,
260
+ _,
261
+ v
262
+ ],
263
+ imports: [
264
+ l,
265
+ r,
266
+ a,
267
+ m,
268
+ s
269
+ ],
270
+ providers: [
271
+ h,
272
+ g,
273
+ _,
274
+ v
275
+ ]
276
+ })], y);
277
+ //#endregion
278
+ export { m as RunnerModule, p as RunnerService, h as ValidationDeduplicationService, g as ValidationFindingsService, y as ValidationModule, _ as ValidationScoringService, v as ValidationService };
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "author": "Jimmy Paolini",
3
+ "bugs": {
4
+ "url": "https://github.com/Organizzolini/codebase/issues"
5
+ },
6
+ "dependencies": {
7
+ "@conformetry/configuration": "0.0.1",
8
+ "@conformetry/core": "0.0.1",
9
+ "@conformetry/languages": "0.0.1",
10
+ "@nestjs/common": "^11.1.21 || ^12.0.1",
11
+ "reflect-metadata": "^0.2.2"
12
+ },
13
+ "description": "Template matching, existence verification, and conformance validation orchestrator for Conformetry",
14
+ "devDependencies": {
15
+ "@golevelup/ts-vitest": "^4.0.0",
16
+ "@nestjs/testing": "^12.0.1",
17
+ "@nx/eslint-plugin": "^23.0.0",
18
+ "@swc/helpers": "^0.5.21",
19
+ "@types/node": "^26.5.1",
20
+ "@vitest/coverage-v8": "^5.0.0",
21
+ "typescript": "^6.0.3",
22
+ "vitest": "^5.0.0"
23
+ },
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/src/index.d.ts",
27
+ "default": "./dist/src/index.js"
28
+ },
29
+ "./package.json": "./package.json"
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "LICENSE",
34
+ "README.md"
35
+ ],
36
+ "homepage": "https://github.com/Organizzolini/codebase/tree/main/packages/ic-suite/conformetry/conformetry-validation#readme",
37
+ "keywords": [
38
+ "conformance",
39
+ "conformetry",
40
+ "matching",
41
+ "orchestration",
42
+ "validation"
43
+ ],
44
+ "license": "MIT",
45
+ "main": "./dist/src/index.js",
46
+ "name": "@conformetry/validation",
47
+ "publishConfig": {
48
+ "access": "public",
49
+ "registry": "https://registry.npmjs.org"
50
+ },
51
+ "repository": {
52
+ "directory": "packages/ic-suite/conformetry/conformetry-validation",
53
+ "type": "git",
54
+ "url": "git+https://github.com/Organizzolini/codebase.git"
55
+ },
56
+ "type": "module",
57
+ "typeCoverage": {
58
+ "atLeast": 100,
59
+ "strict": true
60
+ },
61
+ "types": "./dist/src/index.d.ts",
62
+ "version": "0.0.1"
63
+ }