@conformetry/languages 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.
@@ -0,0 +1,977 @@
1
+ import { ConfigurationService } from '@conformetry/configuration';
2
+ import { ConformetryDifference } from '@conformetry/core';
3
+ import { ConformetryDifferenceLanguage } from '@conformetry/core';
4
+ import { ConformetryDifferenceType } from '@conformetry/core';
5
+ import { ConformetryLanguageValidator } from '@conformetry/core';
6
+ import { DocumentValidationResult } from '@conformetry/core';
7
+ import { LanguageValidatorDescriptor } from '@conformetry/core';
8
+ import { MatchedInstance } from '@conformetry/configuration';
9
+ import { Node } from 'typescript';
10
+ import { PreparedValidationDocument } from '@conformetry/core';
11
+ import { SourceFile } from 'typescript';
12
+ import { ValidationFileResult } from '@conformetry/core';
13
+
14
+ /** Arguments for building a missing-directory conformance error. */
15
+ declare interface BuildMissingDirectoryErrorArguments {
16
+ readonly instanceDirectoryPath: string;
17
+ readonly templateDirectoryPath: string;
18
+ }
19
+
20
+ /** Arguments for building a missing-file conformance error. */
21
+ declare interface BuildMissingFileErrorArguments {
22
+ readonly instanceFilePath: string;
23
+ readonly templateFilePath: string;
24
+ }
25
+
26
+ /** Arguments for turning a weight pair into a score. */
27
+ export declare interface CalculateScoreArguments {
28
+ /** Combined weight of the requirements the instance failed. */
29
+ readonly failedWeight: number;
30
+ /** Combined weight of the requirements that were checked. */
31
+ readonly totalWeight: number;
32
+ }
33
+
34
+ /** Arguments for checking matched instances against the templates they matched. */
35
+ export declare interface CheckInstanceFilesArguments {
36
+ readonly instances: MatchedInstance[];
37
+ }
38
+
39
+ /** What comparing the comments of two source files produced. */
40
+ declare interface CommentComparison {
41
+ readonly missingComments: ExtractedComment[];
42
+ /** Number of template comments checked; each counts as one requirement. */
43
+ readonly totalWeight: number;
44
+ }
45
+
46
+ /** Arguments for walking one level of two markdown trees. */
47
+ declare interface CompareChildrenArguments {
48
+ readonly instanceChildren: MarkdownNode[];
49
+ readonly templateChildren: MarkdownNode[];
50
+ }
51
+
52
+ /** The outcome of walking one level of two trees. */
53
+ declare interface CompareChildrenResult {
54
+ readonly differences: MarkdownComparisonError[];
55
+ /** Template nodes the level weighed the instance against. */
56
+ readonly totalWeight: number;
57
+ }
58
+
59
+ /** Arguments for comparing the comments of two source files. */
60
+ declare interface CompareCommentsArguments {
61
+ readonly instanceSourceFile: SourceFile;
62
+ readonly templateSourceFile: SourceFile;
63
+ }
64
+
65
+ /** Arguments for comparing two JSON values. */
66
+ declare interface CompareJsonArguments {
67
+ readonly instanceValue: JsonValue;
68
+ readonly language: JsonComparisonLanguage;
69
+ readonly pathSegments?: JsonPathSegment[];
70
+ readonly templateValue: JsonValue;
71
+ }
72
+
73
+ /** Arguments for walking two syntax trees in parallel. */
74
+ declare interface CompareTreeArguments {
75
+ readonly instanceNode: Node;
76
+ readonly templateNode: Node;
77
+ }
78
+
79
+ /**
80
+ * Weight a finding carries when it does not declare one.
81
+ *
82
+ * Right for any leaf requirement — a missing line, a missing comment — which
83
+ * is most of them. Only a finding standing in for a whole subtree needs to say
84
+ * otherwise.
85
+ */
86
+ export declare const DEFAULT_ERROR_WEIGHT = 1;
87
+
88
+ /**
89
+ * Owns construction and narrowing of structured conformance differences.
90
+ *
91
+ * Imported by every validator package so error shapes stay identical across
92
+ * languages, and by the file-existence pass for the file and directory
93
+ * categories.
94
+ */
95
+ export declare class DifferencesModule {
96
+ }
97
+
98
+ /**
99
+ * Builds and narrows structured conformance differences.
100
+ *
101
+ * Every validator funnels through here so that error wording, the `fix`
102
+ * suggestion, and the location fields stay consistent across languages. The
103
+ * `resolve*` guards additionally narrow untrusted payloads — notably the JSON
104
+ * emitted by the Python validator bridge, whose fields cross a process
105
+ * boundary and cannot be trusted to match the TypeScript types.
106
+ */
107
+ export declare class DifferencesService {
108
+ constructor();
109
+ /**
110
+ * Builds the error raised when a template directory has no counterpart in
111
+ * the instance tree. Carries no `language`, since a missing directory is not
112
+ * attributable to any one file format.
113
+ */
114
+ buildMissingDirectoryDifference(args: BuildMissingDirectoryErrorArguments): ConformetryDifference;
115
+ /**
116
+ * Builds the error raised when a template file has no counterpart in the
117
+ * instance tree. This is the one check that runs for every template file
118
+ * regardless of extension, so extension-less files such as `.gitignore` are
119
+ * covered too.
120
+ */
121
+ buildMissingFileDifference(args: BuildMissingFileErrorArguments): ConformetryDifference;
122
+ /**
123
+ * Narrows an untrusted value to a known error category, falling back to
124
+ * `"code"`. Falling back rather than throwing keeps one malformed error from
125
+ * failing an entire validation run.
126
+ */
127
+ resolveDifferenceType(value: unknown): ConformetryDifferenceType;
128
+ /**
129
+ * Narrows an untrusted value to a known error language, returning
130
+ * `undefined` when it matches none. Callers omit the field entirely rather
131
+ * than storing a bogus language.
132
+ */
133
+ resolveErrorLanguage(value: unknown): ConformetryDifferenceLanguage | undefined;
134
+ }
135
+
136
+ /**
137
+ * Score given to a document that imposes no requirements at all.
138
+ *
139
+ * An empty template asks for nothing, and nothing is exactly what the instance
140
+ * supplied, so it conforms perfectly. Scoring it zero would be arithmetic
141
+ * leaking into the answer: a run whose templates happen to be empty would fail
142
+ * every threshold while having found no fault with anything.
143
+ */
144
+ export declare const EMPTY_TEMPLATE_SCORE = 1;
145
+
146
+ /** A comment extracted from a source file, with its offset for ordering. */
147
+ declare interface ExtractedComment {
148
+ readonly position: number;
149
+ readonly text: string;
150
+ }
151
+
152
+ /**
153
+ * What the existence pass found, and how much it asked for.
154
+ *
155
+ * The total counts every declared file, present ones included: a template
156
+ * asking for twenty files and getting nineteen has lost a twentieth of itself,
157
+ * which only the full denominator can say.
158
+ */
159
+ export declare interface FilesCheckResult {
160
+ readonly fileResults: ValidationFileResult[];
161
+ /** One requirement per file the matched templates declare. */
162
+ readonly totalWeight: number;
163
+ }
164
+
165
+ /**
166
+ * Provides file and directory existence checking.
167
+ *
168
+ * Imported by `conformetry-validation`, which runs it before delegating to any
169
+ * language validator — a file that is absent cannot be compared.
170
+ */
171
+ export declare class FilesModule {
172
+ }
173
+
174
+ /**
175
+ * Checks that every file a project's template declares actually exists.
176
+ *
177
+ * This runs before any language validator, and is the only check that covers
178
+ * *every* template file regardless of extension. A language validator only
179
+ * sees documents whose extension it claims, so files such as `.gitignore`,
180
+ * `.env.default`, and `pyproject.toml` were previously never checked at all —
181
+ * a project could delete them and still validate clean.
182
+ */
183
+ export declare class FilesService {
184
+ private readonly configurationService;
185
+ private readonly errorsService;
186
+ constructor(configurationService: ConfigurationService, errorsService: DifferencesService);
187
+ /**
188
+ * Reports a path as a missing directory when the template entry lives under
189
+ * a directory that does not exist, and as a missing file otherwise.
190
+ *
191
+ * Reporting the absent directory once is more useful than reporting each of
192
+ * the twenty files inside it.
193
+ */
194
+ private buildMissingDifference;
195
+ /** Counts how many declared files one directory should hold. */
196
+ private countExpectedFiles;
197
+ /**
198
+ * Reports every file a matched instance's template requires but the instance
199
+ * lacks.
200
+ *
201
+ * Missing directories are collapsed to one finding each, so deleting a whole
202
+ * module reports the directory rather than each file within it.
203
+ */
204
+ checkInstanceFiles(args: CheckInstanceFilesArguments): FilesCheckResult;
205
+ }
206
+
207
+ /** What structurally comparing two JSON values produced. */
208
+ export declare interface JsonComparison {
209
+ readonly differences: ConformetryDifference[];
210
+ /** Template nodes the walk weighed the instance against. */
211
+ readonly totalWeight: number;
212
+ }
213
+
214
+ /**
215
+ * Which validator is asking for the comparison.
216
+ *
217
+ * The walk is shared between JSON files and Jupyter notebooks, and the differences
218
+ * it emits must be attributed to whichever one raised them.
219
+ */
220
+ declare type JsonComparisonLanguage = "json" | "python";
221
+
222
+ /**
223
+ * Structurally compares two JSON documents.
224
+ *
225
+ * The template is treated as a **subset** requirement, not an exact match: the
226
+ * instance may add keys and array entries freely, but every key and value the
227
+ * template declares must be present. That is what makes a generated file
228
+ * editable after generation without immediately failing validation.
229
+ *
230
+ * Exported from this module so the Jupyter module can reuse it for
231
+ * notebooks, which are JSON documents, rather than duplicating the walk.
232
+ *
233
+ * The walk also counts what it asked for: every template node is one
234
+ * requirement, and a key with no counterpart costs the whole subtree beneath
235
+ * it, so dropping an object scores worse than dropping one of its scalars.
236
+ */
237
+ export declare class JsonComparisonService {
238
+ private readonly scoringService;
239
+ constructor(scoringService: ScoringService);
240
+ /** Builds one structural error at a JSON path. */
241
+ private buildError;
242
+ /** Merges sibling comparisons into one. */
243
+ private combine;
244
+ /** Matches one required array entry against the instance array. */
245
+ private compareArrayItem;
246
+ /**
247
+ * Compares two arrays.
248
+ *
249
+ * Required scalars must appear somewhere in the instance array, order
250
+ * independent. For a required object, the instance entry that produces the
251
+ * fewest differences is taken as the intended match — an array entry has no key, so
252
+ * there is nothing better to match on.
253
+ */
254
+ private compareArrays;
255
+ /** Compares two objects, requiring every template key to be present. */
256
+ private compareObjects;
257
+ /**
258
+ * Adds the container's own requirement to what its members contributed, so a
259
+ * matched object weighs exactly what `countNodes` would have charged for it
260
+ * had it been missing.
261
+ */
262
+ private countContainer;
263
+ /** Counts a JSON value and every value nested inside it. */
264
+ private countNodes;
265
+ /** Renders a path as `scripts.build[0]` for error messages. */
266
+ private formatPath;
267
+ /** Returns whether a value is a plain JSON object. */
268
+ private isJsonObject;
269
+ /** Returns whether a value is a JSON scalar. */
270
+ private isJsonPrimitive;
271
+ /**
272
+ * Picks the candidate comparison that left the least of the template
273
+ * unaccounted for.
274
+ *
275
+ * Weighed by failed weight rather than error count: one finding standing in
276
+ * for a whole missing object is a worse match than two missing scalars.
277
+ */
278
+ private pickClosestMatch;
279
+ /**
280
+ * Compares a template value against an instance value, returning every way
281
+ * the instance fails to contain what the template requires.
282
+ */
283
+ compare(args: CompareJsonArguments): JsonComparison;
284
+ }
285
+
286
+ /**
287
+ * Provides the JSON language validator.
288
+ *
289
+ * `JsonComparisonService` is exported as well, because notebooks are JSON
290
+ * documents and the Jupyter module reuses the same structural walk.
291
+ */
292
+ export declare class JsonModule {
293
+ }
294
+
295
+ /** One step of a JSON path: an object key or an array index. */
296
+ declare type JsonPathSegment = number | string;
297
+
298
+ /**
299
+ * Checks that a JSON or JSONC file contains everything its template declares.
300
+ *
301
+ * Parsing goes through `jsonc-parser` so a `tsconfig.json` with comments is
302
+ * read the same way TypeScript reads it.
303
+ */
304
+ export declare class JsonService implements ConformetryLanguageValidator {
305
+ private readonly jsonComparisonService;
306
+ constructor(jsonComparisonService: JsonComparisonService);
307
+ readonly descriptor: LanguageValidatorDescriptor;
308
+ /** Reports every key or value the template requires and the instance lacks. */
309
+ validateDocument(document: PreparedValidationDocument): DocumentValidationResult;
310
+ }
311
+
312
+ /** Any JSON value, used when structurally comparing two documents. */
313
+ export declare type JsonValue = boolean | JsonValue[] | null | number | string | {
314
+ [key: string]: JsonValue;
315
+ };
316
+
317
+ /**
318
+ * Provides the Jupyter notebook validator.
319
+ *
320
+ * Composes the JSON, markdown, and Python validators rather than
321
+ * reimplementing any of them — a notebook is all three formats at once.
322
+ */
323
+ export declare class JupyterModule {
324
+ }
325
+
326
+ /**
327
+ * Reads the `.ipynb` format into something comparable.
328
+ *
329
+ * A notebook is JSON, but its meaning lives in the cells: markdown prose and
330
+ * Python code stored as line arrays. This service turns that back into text so
331
+ * the markdown and Python validators can work on it unchanged.
332
+ */
333
+ export declare class JupyterNotebookService {
334
+ constructor();
335
+ /** Buckets each cell's text by kind, preserving notebook order. */
336
+ private groupSourcesByKind;
337
+ /** Narrows a cell's declared kind, defaulting to raw. */
338
+ private readCellKind;
339
+ /** Joins a cell's `source` line array back into text. */
340
+ private readCellSource;
341
+ /**
342
+ * Pairs template cells with instance cells of the same kind, in order.
343
+ *
344
+ * Cells are positional — a notebook has no cell identifiers — so the nth
345
+ * markdown cell is compared with the nth markdown cell. An instance may add
346
+ * cells at the end; it may not drop one the template declares.
347
+ */
348
+ pairCells(args: {
349
+ instanceNotebook: ParsedNotebook;
350
+ templateNotebook: ParsedNotebook;
351
+ }): {
352
+ missingCells: PairedCells[];
353
+ pairedCells: PairedCells[];
354
+ };
355
+ /**
356
+ * Parses notebook JSON, tolerating a malformed file by reporting no cells.
357
+ *
358
+ * A notebook that will not parse is reported by the structural pass, so
359
+ * throwing here would only duplicate that as a crash.
360
+ */
361
+ parseNotebook(content: string): ParsedNotebook;
362
+ }
363
+
364
+ /**
365
+ * Checks that a Jupyter notebook conforms to its template.
366
+ *
367
+ * A notebook is three formats at once, so this validator composes the three
368
+ * that already exist rather than reimplementing any: JSON for the envelope,
369
+ * markdown for prose cells, and the Python bridge for code cells. Validating a
370
+ * code cell through Python's own parser is what lets a notebook be re-run and
371
+ * reformatted without failing.
372
+ */
373
+ export declare class JupyterService implements ConformetryLanguageValidator {
374
+ private readonly jsonComparisonService;
375
+ private readonly jupyterNotebookService;
376
+ private readonly markdownService;
377
+ private readonly pythonBridgeService;
378
+ constructor(jsonComparisonService: JsonComparisonService, jupyterNotebookService: JupyterNotebookService, markdownService: MarkdownService, pythonBridgeService: PythonBridgeService);
379
+ readonly descriptor: LanguageValidatorDescriptor;
380
+ /** Prefixes an error's message so a reader knows which cell it came from. */
381
+ private attributeToCell;
382
+ /** Reduces a notebook to the envelope keys that are compared structurally. */
383
+ private readEnvelope;
384
+ /** Validates one paired cell with the validator matching its kind. */
385
+ private validateCell;
386
+ /**
387
+ * Weighs a cell the notebook does not have.
388
+ *
389
+ * Measured by comparing the template cell against an empty instance, which
390
+ * is exactly what "none of this is present" means. Guessing a flat 1 instead
391
+ * would let a notebook drop a forty-line code cell for the same price as an
392
+ * empty one, and mixing in a line count would put a unit in the denominator
393
+ * that nothing else uses.
394
+ */
395
+ private weighMissingCell;
396
+ /** Reports every notebook difference: envelope, missing cells, cell contents. */
397
+ validateDocument(document: PreparedValidationDocument): DocumentValidationResult;
398
+ }
399
+
400
+ /**
401
+ * The one module a host imports to get every Language.
402
+ *
403
+ * Wiring the Languages individually is still possible — each has its own
404
+ * module — but a host almost always wants all of them, because the Fallback
405
+ * makes the text Language a floor under every run rather than an option.
406
+ */
407
+ export declare class LanguagesModule {
408
+ }
409
+
410
+ /**
411
+ * Answers which Languages a run needs, and applies the Fallback.
412
+ *
413
+ * The Languages are injected rather than looked up by name, so this is the
414
+ * only place one has to be registered: adding a Language means adding it to
415
+ * `claimingLanguages` below, and its own descriptor says which extensions it
416
+ * claims. There is no second list of extensions to keep in step.
417
+ */
418
+ export declare class LanguagesService {
419
+ private readonly jsonService;
420
+ private readonly jupyterService;
421
+ private readonly markdownService;
422
+ private readonly pythonService;
423
+ private readonly textService;
424
+ private readonly typescriptService;
425
+ constructor(jsonService: JsonService, jupyterService: JupyterService, markdownService: MarkdownService, pythonService: PythonService, textService: TextService, typescriptService: TypescriptService);
426
+ /**
427
+ * Every Language that claims extensions of its own, in report order.
428
+ *
429
+ * A method rather than a field, because the injected engines are assigned
430
+ * in the constructor and field initializers have already run by then.
431
+ *
432
+ * The text Language is deliberately absent. It is the Fallback, reached
433
+ * through `widenFallback` below, so it joins a run only when some extension
434
+ * needs it rather than on account of its own `.txt`.
435
+ */
436
+ private claimingLanguages;
437
+ /**
438
+ * The text Language, widened to also claim the extensions nothing else did.
439
+ *
440
+ * Widening the descriptor rather than special-casing the dispatch means the
441
+ * caller routes documents by extension exactly as it does for every other
442
+ * Language.
443
+ */
444
+ private widenFallback;
445
+ /**
446
+ * Resolves a validator for every extension in play.
447
+ *
448
+ * A Language is returned when the run holds at least one extension it
449
+ * claims, so a run over JSON alone never reports a TypeScript result it had
450
+ * nothing to say about. An extension nobody claims falls back to text,
451
+ * compared line by line, so no template file goes unchecked — silently
452
+ * skipping one would check less than the caller believes.
453
+ */
454
+ resolveValidators(args: ResolveValidatorsArguments): ConformetryLanguageValidator[];
455
+ }
456
+
457
+ /** A required markdown node the instance does not contain. */
458
+ declare interface MarkdownComparisonError {
459
+ /** 1-based line in the instance where the node was expected. */
460
+ readonly instanceLine: number | undefined;
461
+ readonly nodeType: string;
462
+ readonly text: string;
463
+ /** Template nodes this one finding stands in for — the missing subtree. */
464
+ readonly weight: number;
465
+ }
466
+
467
+ /**
468
+ * Provides the markdown language validator.
469
+ *
470
+ * Imported by the Languages module, and by the Jupyter module, which reuses
471
+ * it for a notebook's markdown cells.
472
+ */
473
+ export declare class MarkdownModule {
474
+ }
475
+
476
+ /**
477
+ * The subset of an mdast node this validator reads.
478
+ *
479
+ * Declared structurally rather than importing mdast's own types because only
480
+ * these fields participate in matching, and a narrow shape keeps the
481
+ * comparison honest about what it actually inspects.
482
+ */
483
+ declare interface MarkdownNode {
484
+ readonly alt?: string;
485
+ readonly children?: MarkdownNode[];
486
+ readonly depth?: number;
487
+ readonly lang?: string;
488
+ readonly ordered?: boolean;
489
+ readonly position?: {
490
+ readonly end?: {
491
+ readonly line?: number;
492
+ };
493
+ };
494
+ readonly type: string;
495
+ readonly url?: string;
496
+ readonly value?: string;
497
+ }
498
+
499
+ /**
500
+ * Decides whether two markdown nodes are "the same node".
501
+ *
502
+ * Each node type has its own notion of identity: a heading is its depth plus
503
+ * its text, a link is its URL plus its text, a table is its column count. This
504
+ * is what lets a template require *a table with three columns* without
505
+ * dictating its contents.
506
+ */
507
+ export declare class MarkdownNodesService {
508
+ constructor();
509
+ /**
510
+ * How to decide that two nodes of a given type are "the same node".
511
+ *
512
+ * A table rather than a switch so each rule stays its own small function —
513
+ * one branch per markdown type in a single method is unreadable, and the
514
+ * types that need no special rule fall through to a text comparison.
515
+ */
516
+ private readonly matchersByType;
517
+ /** Counts a table's columns from its first row. */
518
+ private readColumnCount;
519
+ /** Compares two optional string fields, treating absent as empty. */
520
+ private sameField;
521
+ /**
522
+ * Counts a node and every countable node beneath it.
523
+ *
524
+ * This is what a missing node costs. Comparison reports a vanished section
525
+ * once, but the template asked for the section and everything inside it, so
526
+ * weighing the finding by its subtree keeps a deleted table from scoring the
527
+ * same as a deleted heading.
528
+ *
529
+ * Skipped types are excluded on both sides of the fraction, so nodes the
530
+ * comparison never checks cannot dilute a score.
531
+ */
532
+ countSubtree(node: MarkdownNode): number;
533
+ /** Narrows a raw mdast child list to the nodes this validator understands. */
534
+ filterNodes(children: readonly unknown[]): MarkdownNode[];
535
+ /** Returns whether an instance node satisfies a template node. */
536
+ matches(args: {
537
+ instanceNode: MarkdownNode;
538
+ templateNode: MarkdownNode;
539
+ }): boolean;
540
+ /** Reads a node's children, or an empty list for a leaf. */
541
+ readChildren(node: MarkdownNode): MarkdownNode[];
542
+ /** Reads a node's rendered plain text. */
543
+ readText(node: MarkdownNode): string;
544
+ }
545
+
546
+ /**
547
+ * Checks that a markdown file contains every structure its template declares.
548
+ *
549
+ * Comparison is structural rather than textual: headings, code fences, links,
550
+ * and tables are matched as mdast nodes, so reformatting or reflowing prose
551
+ * does not fail validation while deleting a required section does.
552
+ */
553
+ export declare class MarkdownService implements ConformetryLanguageValidator {
554
+ private readonly markdownNodesService;
555
+ private readonly markdownTreeService;
556
+ constructor(markdownNodesService: MarkdownNodesService, markdownTreeService: MarkdownTreeService);
557
+ /** GitHub-flavored so tables and task lists parse as their own node types. */
558
+ private readonly processor;
559
+ readonly descriptor: LanguageValidatorDescriptor;
560
+ /** Reports every markdown structure the template requires and the file lacks. */
561
+ validateDocument(document: PreparedValidationDocument): DocumentValidationResult;
562
+ }
563
+
564
+ /**
565
+ * Walks two markdown trees and reports what the template requires but the
566
+ * instance lacks.
567
+ *
568
+ * The walk is order-preserving but not position-locked: each template node is
569
+ * matched against any instance sibling, and the last match anchors the error
570
+ * location for whatever follows. A document may therefore add sections freely,
571
+ * as long as it still contains everything the template declares.
572
+ *
573
+ * The walk also counts what it asked for: every template node it weighs is one
574
+ * requirement, and a node with no counterpart costs its whole subtree.
575
+ */
576
+ export declare class MarkdownTreeService {
577
+ private readonly markdownNodesService;
578
+ private readonly scoringService;
579
+ constructor(markdownNodesService: MarkdownNodesService, scoringService: ScoringService);
580
+ /** Describes a template node the instance does not contain. */
581
+ private buildError;
582
+ /**
583
+ * Matches a container node, then descends into it.
584
+ *
585
+ * Several instance nodes may match the container shape — two lists, say — so
586
+ * the one whose children satisfy the most of the template is chosen.
587
+ */
588
+ private compareContainer;
589
+ /** Matches a leaf node on its own identity, without descending. */
590
+ private compareLeaf;
591
+ /** Finds every instance sibling satisfying the template node. */
592
+ private findCandidates;
593
+ /** Compares one level of two trees, descending into containers. */
594
+ compareChildren(args: CompareChildrenArguments): CompareChildrenResult;
595
+ }
596
+
597
+ /**
598
+ * One notebook cell, in the subset of the `.ipynb` schema this validator uses.
599
+ *
600
+ * `source` is a list of lines with their newlines retained, which is how
601
+ * Jupyter stores it.
602
+ */
603
+ declare interface NotebookCell {
604
+ readonly cell_type?: unknown;
605
+ readonly source?: unknown;
606
+ }
607
+
608
+ /** The cell kinds this validator understands. */
609
+ declare type NotebookCellKind = "code" | "markdown" | "raw";
610
+
611
+ /** A pair of template and instance cells of the same kind, in order. */
612
+ declare interface PairedCells {
613
+ readonly index: number;
614
+ readonly instanceSource: string;
615
+ readonly kind: NotebookCellKind;
616
+ readonly templateSource: string;
617
+ }
618
+
619
+ /** A notebook reduced to the parts that are validated. */
620
+ declare interface ParsedNotebook {
621
+ readonly cells: NotebookCell[];
622
+ }
623
+
624
+ /** The highest possible score: every template requirement is honoured. */
625
+ export declare const PERFECT_SCORE = 1;
626
+
627
+ /**
628
+ * Runs Python conformance checks through the Python interpreter.
629
+ *
630
+ * Python's syntax tree is only available from Python, so structural validation
631
+ * shells out to a small module shipped inside this package rather than
632
+ * approximating it with a line comparison. The subprocess is synchronous
633
+ * because callers are, and because one file's validation has nothing to
634
+ * overlap with.
635
+ */
636
+ export declare class PythonBridgeService {
637
+ private readonly errorsService;
638
+ private readonly scoringService;
639
+ constructor(errorsService: DifferencesService, scoringService: ScoringService);
640
+ /**
641
+ * Directory holding the `python` package, resolved from this module rather
642
+ * than from the workspace root, so the bridge is found the same way whether
643
+ * conformetry runs from a checkout or from `node_modules`.
644
+ */
645
+ private readonly pythonRootPath;
646
+ /**
647
+ * Wraps a bridge failure as a reportable error rather than throwing.
648
+ *
649
+ * Weighed as a single failed requirement out of one: the file could not be
650
+ * checked at all, so claiming any particular proportion of it conforms would
651
+ * be an invention.
652
+ */
653
+ private buildBridgeError;
654
+ /** Reads the optional location fields, omitting any the bridge left out. */
655
+ private readLocations;
656
+ /** Narrows an untrusted numeric field from the bridge payload. */
657
+ private readNumber;
658
+ /** Narrows an untrusted string field from the bridge payload. */
659
+ private readString;
660
+ /** Reads the optional expected and actual values. */
661
+ private readValues;
662
+ /** Maps one snake_case bridge error onto the shared error shape. */
663
+ private toConformetryDifference;
664
+ /**
665
+ * Compares one Python source against its rendered template.
666
+ *
667
+ * A missing interpreter, a crashed bridge, or unreadable output are all
668
+ * reported as conformance differences: they mean this file could not be checked,
669
+ * which the run should surface, but they must not abort validation of every
670
+ * other file.
671
+ */
672
+ validatePythonSource(args: RunPythonBridgeArguments): DocumentValidationResult;
673
+ }
674
+
675
+ /**
676
+ * Provides the Python language validator.
677
+ *
678
+ * `PythonBridgeService` is exported as well, because a notebook's code cells
679
+ * are Python and the Jupyter module validates them through the same bridge.
680
+ */
681
+ export declare class PythonModule {
682
+ }
683
+
684
+ /**
685
+ * Checks that a Python file declares everything its template requires.
686
+ *
687
+ * Comparison is structural, through Python's own `ast` module, so reformatting
688
+ * a file or reordering its declarations does not fail validation while
689
+ * deleting a required class or function does.
690
+ */
691
+ export declare class PythonService implements ConformetryLanguageValidator {
692
+ private readonly pythonBridgeService;
693
+ constructor(pythonBridgeService: PythonBridgeService);
694
+ readonly descriptor: LanguageValidatorDescriptor;
695
+ /** Reports every declaration and comment the template requires. */
696
+ validateDocument(document: PreparedValidationDocument): DocumentValidationResult;
697
+ }
698
+
699
+ /** Arguments for resolving the Languages one validation run needs. */
700
+ export declare interface ResolveValidatorsArguments {
701
+ /**
702
+ * Every distinct file extension the run's templates declare, including the
703
+ * leading dot. Extensions no Language claims are routed to the Fallback.
704
+ */
705
+ readonly extensions: string[];
706
+ }
707
+
708
+ /** Arguments for one bridge invocation. */
709
+ declare interface RunPythonBridgeArguments {
710
+ readonly filename: string;
711
+ readonly instance: string;
712
+ /** Template source, already rendered on the TypeScript side. */
713
+ readonly template: string;
714
+ }
715
+
716
+ /**
717
+ * Owns the conformance arithmetic every validator and host would otherwise
718
+ * repeat: what a finding weighs, and what a weight pair scores.
719
+ */
720
+ export declare class ScoringModule {
721
+ }
722
+
723
+ /**
724
+ * Turns weights into conformance scores.
725
+ *
726
+ * The arithmetic is small but it is the same arithmetic in seven places — one
727
+ * per language package, plus the file-existence pass and the run aggregate —
728
+ * and it has two edge cases worth getting right once: the default weight of a
729
+ * finding that declares none, and an empty template whose denominator is zero.
730
+ */
731
+ export declare class ScoringService {
732
+ constructor();
733
+ /**
734
+ * Returns the share of the checked requirements the instance honoured, from
735
+ * 0 to 1.
736
+ *
737
+ * Clamped at both ends. A validator that double-counts an overlapping
738
+ * requirement could otherwise report a failed weight above the total and
739
+ * produce a negative score, which would read as a much worse instance than
740
+ * one that is simply entirely wrong.
741
+ */
742
+ calculateScore(args: CalculateScoreArguments): number;
743
+ /** Adds up what a set of findings costs, defaulting each to its own weight. */
744
+ sumWeights(differences: readonly WeightedFinding[]): number;
745
+ }
746
+
747
+ /**
748
+ * Provides the text validator service.
749
+ */
750
+ export declare class TextModule {
751
+ }
752
+
753
+ /**
754
+ * Checks that a text file contains every line its template requires.
755
+ *
756
+ * Matching is duplicate-aware: a template line that appears twice must appear
757
+ * twice in the instance. Order is not enforced, so a file may add lines
758
+ * anywhere — the template is a lower bound, not an exact specification.
759
+ */
760
+ export declare class TextService implements ConformetryLanguageValidator {
761
+ constructor();
762
+ readonly descriptor: LanguageValidatorDescriptor;
763
+ /** Counts how many times each line occurs, for duplicate-aware matching. */
764
+ private countLines;
765
+ /** Finds template lines the instance does not supply often enough. */
766
+ private findMissingLines;
767
+ /**
768
+ * Reports every template line missing from the instance.
769
+ *
770
+ * Every template line is one requirement, blank ones included: this
771
+ * validator matches them literally, so a blank line the instance does not
772
+ * supply is a real miss and counting it keeps the denominator honest.
773
+ */
774
+ validateDocument(document: PreparedValidationDocument): DocumentValidationResult;
775
+ }
776
+
777
+ /** What comparing two syntax trees produced. */
778
+ declare interface TreeComparison {
779
+ readonly differences: TypescriptComparisonError[];
780
+ /** Number of template nodes the walk weighed the instance against. */
781
+ readonly totalWeight: number;
782
+ }
783
+
784
+ /**
785
+ * Checks that a file carries the comments its template requires, in order.
786
+ *
787
+ * Order matters here in a way it does not for declarations: the section
788
+ * markers (`🏗 Dependency Injection`, `🔏 Private Methods`, …) are comments,
789
+ * and a file that has them all but in the wrong order has not adopted the
790
+ * layout. Matching is therefore a subsequence check, not a set check.
791
+ */
792
+ export declare class TypescriptCommentsService {
793
+ constructor();
794
+ /**
795
+ * Reports each template comment absent from the instance, or present but
796
+ * out of order relative to the comments before it.
797
+ *
798
+ * Every template comment is one requirement, whatever its length: a section
799
+ * marker is a marker. That is reported alongside the misses so a document
800
+ * whose comments are all present still contributes them to its score.
801
+ */
802
+ compareComments(args: CompareCommentsArguments): CommentComparison;
803
+ /**
804
+ * Collects every comment in a source file, in source order.
805
+ *
806
+ * Walks down to individual tokens and reads the trivia preceding each one.
807
+ * Every comment precedes some token, including the closing brace of a class,
808
+ * which is what makes this complete.
809
+ *
810
+ * Two simpler approaches fail here. Reading leading and trailing ranges off
811
+ * *statement* nodes misses any comment bordering no node — notably the
812
+ * section markers between the last class member and the closing brace, where
813
+ * `🔏 Private Methods` and `🌎 Public Methods` live in an otherwise empty
814
+ * service; those markers were silently unenforceable. Scanning the raw token
815
+ * stream instead loses sync on template literals, because a substitution's
816
+ * closing brace needs an explicit re-scan, and everything after the first
817
+ * template substitution is mis-tokenized.
818
+ */
819
+ extractComments(sourceFile: SourceFile): ExtractedComment[];
820
+ }
821
+
822
+ /** A declaration the template requires and the instance does not contain. */
823
+ declare interface TypescriptComparisonError {
824
+ /** Offset in the instance file to anchor the error at, when known. */
825
+ readonly instancePosition: number | undefined;
826
+ /** Syntax-kind label, e.g. `ClassDeclaration`. */
827
+ readonly kindLabel: string;
828
+ /** The node's key — an import specifier, a member name — when it has one. */
829
+ readonly nodeKey: string | undefined;
830
+ /** Offset in the rendered template where the requirement is declared. */
831
+ readonly templatePosition: number | undefined;
832
+ /** Template nodes this one finding stands in for — the missing subtree. */
833
+ readonly weight: number;
834
+ }
835
+
836
+ /**
837
+ * Provides the TypeScript language validator.
838
+ *
839
+ * Split into node keying, tree walking, and comment comparison so each concern
840
+ * stays independently testable — the same decomposition the previous
841
+ * conformance tool used.
842
+ */
843
+ export declare class TypescriptModule {
844
+ }
845
+
846
+ /**
847
+ * Derives a stable identity for a syntax node.
848
+ *
849
+ * Matching by key rather than by position is what lets a file reorder its
850
+ * members, or add new ones, without failing validation: an import is
851
+ * identified by its module specifier, a member by its name, a decorator by its
852
+ * dotted callee. Nodes with no meaningful key fall back to matching on syntax
853
+ * kind alone.
854
+ */
855
+ export declare class TypescriptNodesService {
856
+ constructor();
857
+ /** Builds a dotted name such as `Nest.Injectable` from a callee expression. */
858
+ private buildDottedName;
859
+ /** Returns whether a value looks like a syntax node. */
860
+ private isNode;
861
+ /** Keys a decorator by its callee, so `@Injectable()` matches `@Injectable`. */
862
+ private readDecoratorKey;
863
+ /** Keys an export by its module specifier, when it re-exports one. */
864
+ private readExportKey;
865
+ /**
866
+ * Keys a call statement by its callee plus its first literal argument, so
867
+ * two `describe("...")` blocks are told apart by their subject.
868
+ */
869
+ private readExpressionStatementKey;
870
+ /** Keys an import by its module specifier. */
871
+ private readImportKey;
872
+ /** Reads a literal's text, for nodes that are themselves a value. */
873
+ private readLiteralKey;
874
+ /** Reads a declaration's own name, when it has one. */
875
+ private readNamedKey;
876
+ /**
877
+ * Counts a node and everything beneath it.
878
+ *
879
+ * This is what a missing declaration costs. Comparison reports a vanished
880
+ * class once, but the template asked for the class *and* every member inside
881
+ * it, so weighing that finding by its subtree is what keeps a deleted class
882
+ * from scoring the same as a deleted import.
883
+ */
884
+ countSubtree(node: Node): number;
885
+ /** Reads a node's direct children, skipping the end-of-file token. */
886
+ readChildren(node: Node): Node[];
887
+ /**
888
+ * Reads a node's identity, or `null` when it has none.
889
+ *
890
+ * A `null` key means the node can only be matched by syntax kind — which is
891
+ * why an anonymous statement is satisfied by any statement of that kind.
892
+ */
893
+ readKey(node: Node): null | string;
894
+ /** Reads a node's syntax-kind label, for error messages. */
895
+ readKindLabel(node: Node): string;
896
+ }
897
+
898
+ /**
899
+ * Checks that a TypeScript file declares everything its template requires.
900
+ *
901
+ * Two independent checks run over the same parse: the syntax tree, which
902
+ * verifies imports, decorators, classes, and members exist; and the comments,
903
+ * which verify the section markers appear in the prescribed order.
904
+ */
905
+ export declare class TypescriptService implements ConformetryLanguageValidator {
906
+ private readonly typeScriptCommentsService;
907
+ private readonly typeScriptTreeService;
908
+ constructor(typeScriptCommentsService: TypescriptCommentsService, typeScriptTreeService: TypescriptTreeService);
909
+ readonly descriptor: LanguageValidatorDescriptor;
910
+ /** Parses source text, choosing the dialect from the filename. */
911
+ private parseSourceFile;
912
+ /** Converts a source offset into a 1-based line and column. */
913
+ private readLocation;
914
+ /** Compares the comments and describes each missing section marker. */
915
+ private validateComments;
916
+ /** Compares the syntax trees and describes each missing declaration. */
917
+ private validateStructure;
918
+ /**
919
+ * Reports every declaration and comment the template requires.
920
+ *
921
+ * The two passes weigh independent things — structure counts syntax nodes,
922
+ * comments count section markers — so their totals add rather than one
923
+ * subsuming the other.
924
+ */
925
+ validateDocument(document: PreparedValidationDocument): DocumentValidationResult;
926
+ }
927
+
928
+ /**
929
+ * Walks two syntax trees in parallel and reports what the template requires
930
+ * but the instance does not contain.
931
+ *
932
+ * The template is a **structural subset** requirement: every declaration it
933
+ * makes must exist somewhere in the instance, but the instance may add
934
+ * anything and may order its members freely.
935
+ *
936
+ * The walk also counts what it asked for. Every template node visited is one
937
+ * requirement, and a node with no counterpart costs its whole subtree, so the
938
+ * weight of a finding is proportional to how much of the template went
939
+ * missing rather than to how many findings were printed.
940
+ */
941
+ export declare class TypescriptTreeService {
942
+ private readonly scoringService;
943
+ private readonly typeScriptNodesService;
944
+ constructor(scoringService: ScoringService, typeScriptNodesService: TypescriptNodesService);
945
+ /** Describes a template node with no instance counterpart. */
946
+ private buildError;
947
+ /**
948
+ * Descends into whichever candidate explains the template best.
949
+ *
950
+ * Several instance nodes can share a key or kind — two methods with the same
951
+ * name on different classes, say — so the one leaving the least of the
952
+ * template unaccounted for is taken as the intended match.
953
+ *
954
+ * Weighed by failed weight rather than by error count: one finding standing
955
+ * in for a whole missing class is a worse match than two missing imports,
956
+ * and counting findings would have picked the wrong one.
957
+ */
958
+ private compareBestCandidate;
959
+ /** Matches one template child against the instance's children. */
960
+ private compareChild;
961
+ /** Compares one level of two trees, descending into every match. */
962
+ compareTree(args: CompareTreeArguments): TreeComparison;
963
+ }
964
+
965
+ /**
966
+ * Anything that carries a weight.
967
+ *
968
+ * Deliberately narrower than `ConformetryDifference`: a language package weighs its
969
+ * own internal findings before they are ever described as conformetry differences,
970
+ * and requiring the full error shape would force it to build messages just to
971
+ * count.
972
+ */
973
+ export declare interface WeightedFinding {
974
+ readonly weight?: number;
975
+ }
976
+
977
+ export { }