@shivaedev/quality 0.0.0 → 0.1.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.
Files changed (159) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/LICENSE +21 -0
  3. package/README.md +127 -1
  4. package/dist/baseline/compare.d.ts +26 -0
  5. package/dist/baseline/compare.d.ts.map +1 -0
  6. package/dist/baseline/compare.js +42 -0
  7. package/dist/baseline/compare.js.map +1 -0
  8. package/dist/baseline/format.d.ts +13 -0
  9. package/dist/baseline/format.d.ts.map +1 -0
  10. package/dist/baseline/format.js +32 -0
  11. package/dist/baseline/format.js.map +1 -0
  12. package/dist/baseline/update.d.ts +24 -0
  13. package/dist/baseline/update.d.ts.map +1 -0
  14. package/dist/baseline/update.js +60 -0
  15. package/dist/baseline/update.js.map +1 -0
  16. package/dist/cli/args.d.ts +25 -0
  17. package/dist/cli/args.d.ts.map +1 -0
  18. package/dist/cli/args.js +64 -0
  19. package/dist/cli/args.js.map +1 -0
  20. package/dist/cli/baseline.d.ts +5 -0
  21. package/dist/cli/baseline.d.ts.map +1 -0
  22. package/dist/cli/baseline.js +37 -0
  23. package/dist/cli/baseline.js.map +1 -0
  24. package/dist/cli/lint.d.ts +5 -0
  25. package/dist/cli/lint.d.ts.map +1 -0
  26. package/dist/cli/lint.js +20 -0
  27. package/dist/cli/lint.js.map +1 -0
  28. package/dist/cli/run-main.d.ts +3 -0
  29. package/dist/cli/run-main.d.ts.map +1 -0
  30. package/dist/cli/run-main.js +25 -0
  31. package/dist/cli/run-main.js.map +1 -0
  32. package/dist/cli.d.ts +3 -0
  33. package/dist/cli.d.ts.map +1 -0
  34. package/dist/cli.js +27 -0
  35. package/dist/cli.js.map +1 -0
  36. package/dist/config/decode.d.ts +19 -0
  37. package/dist/config/decode.d.ts.map +1 -0
  38. package/dist/config/decode.js +25 -0
  39. package/dist/config/decode.js.map +1 -0
  40. package/dist/config/load.d.ts +13 -0
  41. package/dist/config/load.d.ts.map +1 -0
  42. package/dist/config/load.js +47 -0
  43. package/dist/config/load.js.map +1 -0
  44. package/dist/config/resolve.d.ts +11 -0
  45. package/dist/config/resolve.d.ts.map +1 -0
  46. package/dist/config/resolve.js +31 -0
  47. package/dist/config/resolve.js.map +1 -0
  48. package/dist/config.d.ts +31 -0
  49. package/dist/config.d.ts.map +1 -0
  50. package/dist/config.js +2 -0
  51. package/dist/config.js.map +1 -0
  52. package/dist/decoded.d.ts +13 -0
  53. package/dist/decoded.d.ts.map +1 -0
  54. package/dist/decoded.js +11 -0
  55. package/dist/decoded.js.map +1 -0
  56. package/dist/engine/evaluate.d.ts +18 -0
  57. package/dist/engine/evaluate.d.ts.map +1 -0
  58. package/dist/engine/evaluate.js +20 -0
  59. package/dist/engine/evaluate.js.map +1 -0
  60. package/dist/engine/run-rules.d.ts +12 -0
  61. package/dist/engine/run-rules.d.ts.map +1 -0
  62. package/dist/engine/run-rules.js +19 -0
  63. package/dist/engine/run-rules.js.map +1 -0
  64. package/dist/engine/session.d.ts +19 -0
  65. package/dist/engine/session.d.ts.map +1 -0
  66. package/dist/engine/session.js +25 -0
  67. package/dist/engine/session.js.map +1 -0
  68. package/dist/engine/violation.d.ts +12 -0
  69. package/dist/engine/violation.d.ts.map +1 -0
  70. package/dist/engine/violation.js +23 -0
  71. package/dist/engine/violation.js.map +1 -0
  72. package/dist/exceptions/registry.d.ts +24 -0
  73. package/dist/exceptions/registry.d.ts.map +1 -0
  74. package/dist/exceptions/registry.js +33 -0
  75. package/dist/exceptions/registry.js.map +1 -0
  76. package/dist/failure.d.ts +21 -0
  77. package/dist/failure.d.ts.map +1 -0
  78. package/dist/failure.js +12 -0
  79. package/dist/failure.js.map +1 -0
  80. package/dist/index.d.ts +6 -0
  81. package/dist/index.d.ts.map +1 -0
  82. package/dist/index.js +3 -0
  83. package/dist/index.js.map +1 -0
  84. package/dist/inventory/collect.d.ts +15 -0
  85. package/dist/inventory/collect.d.ts.map +1 -0
  86. package/dist/inventory/collect.js +34 -0
  87. package/dist/inventory/collect.js.map +1 -0
  88. package/dist/inventory/filesystem.d.ts +16 -0
  89. package/dist/inventory/filesystem.d.ts.map +1 -0
  90. package/dist/inventory/filesystem.js +26 -0
  91. package/dist/inventory/filesystem.js.map +1 -0
  92. package/dist/inventory/ignore-scope.d.ts +17 -0
  93. package/dist/inventory/ignore-scope.d.ts.map +1 -0
  94. package/dist/inventory/ignore-scope.js +26 -0
  95. package/dist/inventory/ignore-scope.js.map +1 -0
  96. package/dist/inventory/walk.d.ts +8 -0
  97. package/dist/inventory/walk.d.ts.map +1 -0
  98. package/dist/inventory/walk.js +38 -0
  99. package/dist/inventory/walk.js.map +1 -0
  100. package/dist/report/plural.d.ts +2 -0
  101. package/dist/report/plural.d.ts.map +1 -0
  102. package/dist/report/plural.js +2 -0
  103. package/dist/report/plural.js.map +1 -0
  104. package/dist/report/render.d.ts +11 -0
  105. package/dist/report/render.d.ts.map +1 -0
  106. package/dist/report/render.js +55 -0
  107. package/dist/report/render.js.map +1 -0
  108. package/dist/report/stale.d.ts +8 -0
  109. package/dist/report/stale.d.ts.map +1 -0
  110. package/dist/report/stale.js +9 -0
  111. package/dist/report/stale.js.map +1 -0
  112. package/dist/rule.d.ts +56 -0
  113. package/dist/rule.d.ts.map +1 -0
  114. package/dist/rule.js +23 -0
  115. package/dist/rule.js.map +1 -0
  116. package/dist/rules/built-in.d.ts +6 -0
  117. package/dist/rules/built-in.d.ts.map +1 -0
  118. package/dist/rules/built-in.js +3 -0
  119. package/dist/rules/built-in.js.map +1 -0
  120. package/dist/rules/max-lines.d.ts +6 -0
  121. package/dist/rules/max-lines.d.ts.map +1 -0
  122. package/dist/rules/max-lines.js +26 -0
  123. package/dist/rules/max-lines.js.map +1 -0
  124. package/dist/standard-schema.d.ts +25 -0
  125. package/dist/standard-schema.d.ts.map +1 -0
  126. package/dist/standard-schema.js +3 -0
  127. package/dist/standard-schema.js.map +1 -0
  128. package/package.json +53 -4
  129. package/src/baseline/compare.ts +81 -0
  130. package/src/baseline/format.ts +53 -0
  131. package/src/baseline/update.ts +83 -0
  132. package/src/cli/args.ts +81 -0
  133. package/src/cli/baseline.ts +52 -0
  134. package/src/cli/lint.ts +27 -0
  135. package/src/cli/run-main.ts +30 -0
  136. package/src/cli.ts +29 -0
  137. package/src/config/decode.ts +35 -0
  138. package/src/config/load.ts +58 -0
  139. package/src/config/resolve.ts +60 -0
  140. package/src/config.ts +33 -0
  141. package/src/decoded.ts +15 -0
  142. package/src/engine/evaluate.ts +39 -0
  143. package/src/engine/run-rules.ts +40 -0
  144. package/src/engine/session.ts +40 -0
  145. package/src/engine/violation.ts +35 -0
  146. package/src/exceptions/registry.ts +58 -0
  147. package/src/failure.ts +13 -0
  148. package/src/index.ts +5 -0
  149. package/src/inventory/collect.ts +53 -0
  150. package/src/inventory/filesystem.ts +47 -0
  151. package/src/inventory/ignore-scope.ts +44 -0
  152. package/src/inventory/walk.ts +58 -0
  153. package/src/report/plural.ts +1 -0
  154. package/src/report/render.ts +85 -0
  155. package/src/report/stale.ts +30 -0
  156. package/src/rule.ts +90 -0
  157. package/src/rules/built-in.ts +3 -0
  158. package/src/rules/max-lines.ts +31 -0
  159. package/src/standard-schema.ts +22 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,29 @@
1
+ # Changelog
2
+
3
+ ## 0.1.1 - 2026-09-26
4
+
5
+ ### Changed
6
+
7
+ - Load only the Node modules the `quality` command uses, so it starts in about
8
+ half the time.
9
+
10
+ ## 0.1.0 - 2026-09-26
11
+
12
+ ### Added
13
+
14
+ - Add the `quality` command line: `lint` runs every rule and prints one report
15
+ grouped by rule, `baseline write` records existing violations and
16
+ `baseline prune` drops fixed debt. It exits 0 when the gate passes, 1 when it
17
+ fails and 2 when it cannot run.
18
+ - Add `defineConfig` for a typed `quality.config.ts`: sources, exclusions in
19
+ `.gitignore` syntax, a level and typed options per rule, and repository rules.
20
+ The config is validated when loaded, and every rule defaults to `error`.
21
+ - Add `defineRule` for repository rules, with options declared by any Standard
22
+ Schema.
23
+ - Add the shrink-only baseline: a baselined file may not gain violations or grow
24
+ past its measure, and an entry that allows more than is left fails until it is
25
+ pruned. Existing baselines adopt a new rule only by name.
26
+ - Add the registry of permanent exceptions: each entry names a rule, a file,
27
+ optionally a subject, and a reason, and an entry that covers nothing fails.
28
+ - Add `structure/max-lines`: 150 lines per source file and 300 per test file by
29
+ default, with configurable limits and test patterns.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ShivaeDev
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 CHANGED
@@ -1,3 +1,129 @@
1
1
  # @shivaedev/quality
2
2
 
3
- This version only reserves the name. Releases are published from https://github.com/ShivaeDev/platform.
3
+ One quality gate for a repository: typed rules, one report, a baseline that only shrinks and a registry of permanent exceptions, each with its reason.
4
+
5
+ Every rule is an error by default. A repository adopts the gate at once: it records its existing violations in the baseline, and from then on a baselined file may not get worse, and fixed debt must leave the baseline.
6
+
7
+ ## Setup
8
+
9
+ ```sh
10
+ pnpm add --save-dev @shivaedev/quality
11
+ ```
12
+
13
+ Node 24 or later loads `quality.config.ts` directly (type stripping), so no build step runs first. Use only erasable TypeScript in the config, and give relative imports their `.ts` extension.
14
+
15
+ ```ts
16
+ // quality.config.ts
17
+ import { defineConfig } from "@shivaedev/quality";
18
+ import { noTodo } from "./quality/no-todo.ts";
19
+
20
+ export default defineConfig({
21
+ sources: ["src", "scripts"],
22
+ exclude: ["src/components/ui/", "*.gen.ts"],
23
+ local: [noTodo],
24
+ rules: {
25
+ "structure/max-lines": { options: { source: 200, testFiles: ["e2e/"] } },
26
+ "local/no-todo": "warn",
27
+ },
28
+ });
29
+ ```
30
+
31
+ | Key | Default | Meaning |
32
+ | --- | --- | --- |
33
+ | `sources` | `["."]` | Directories or files to check, relative to the config. A missing source is an error. |
34
+ | `exclude` | `[]` | Paths never checked, in `.gitignore` syntax. Files ignored by git, `.git` and `node_modules` are always skipped. |
35
+ | `extensions` | TypeScript and JavaScript modules | Which files rules read as sources. |
36
+ | `registry` | `quality/registry.json` | Permanent exceptions. |
37
+ | `baseline` | `quality/baseline.json` | Existing violations that may only shrink. |
38
+ | `local` | `[]` | The repository's own rules, made with `defineRule`. |
39
+ | `rules` | every rule at `error` | A level per rule id, or `{ level, options }`. |
40
+
41
+ The config is typed: an unknown rule id, a misspelled option or an option of the wrong type fails to compile. It is also validated when loaded, so a JavaScript config gets the same checks.
42
+
43
+ ## Levels
44
+
45
+ - `error` is the default. An error-level violation that the baseline and the registry do not cover fails the gate.
46
+ - `warn` reports without failing. It exists for the transition to a new rule and is meant to be temporary: move the rule to `error` and baseline what is left.
47
+ - `off` disables the rule.
48
+
49
+ ## Rules
50
+
51
+ `structure/max-lines` keeps each module to one job: a source file may have 150 lines and a test file 300, counted the way an editor numbers them. Declaration files are exempt. Its options are `source` and `test` (the limits) and `testFiles`, `.gitignore` patterns that mark test files (`*.test.*`, `*.spec.*`, `test/`, `tests/` and `__tests__/` by default). A violation's measure is the file's line count, so a baselined file may shrink but never grow.
52
+
53
+ ### Local rules
54
+
55
+ ```ts
56
+ import { defineRule } from "@shivaedev/quality";
57
+
58
+ export const noTodo = defineRule({
59
+ id: "local/no-todo",
60
+ description: "Resolve TODOs before merging.",
61
+ check: ({ sources }) =>
62
+ sources.flatMap((file) =>
63
+ file.lines.flatMap((text, index) => (text.includes("TODO") ? [{ file: file.path, line: index + 1, message: "Resolve this TODO." }] : [])),
64
+ ),
65
+ });
66
+ ```
67
+
68
+ `check` receives the repository `root`, every checked path in `files`, the `sources` with their `text` and `lines`, `readText(path)` for any other file (undefined when absent) and the validated `options`. It returns findings, or a promise of them. A finding names its `file`, relative to the root (an absolute path under it is made relative), and its `message`, and optionally a `line`, a `subject` that tells apart exceptions of one rule in one file, and a `measure` where larger is worse.
69
+
70
+ A rule with options declares them with any [Standard Schema](https://standardschema.dev), such as `Schema.toStandardSchemaV1(...)` from Effect. Options are an object: when the config gives none, the schema validates `{}`, so give each option a default or make it optional. A rule without an options schema refuses options.
71
+
72
+ ## Baseline
73
+
74
+ ```json
75
+ {
76
+ "structure/max-lines": {
77
+ "src/server/db.ts": {
78
+ "count": 1,
79
+ "measure": 412
80
+ }
81
+ },
82
+ "local/no-todo": {
83
+ "src/legacy/sync.ts": {
84
+ "count": 3
85
+ }
86
+ }
87
+ }
88
+ ```
89
+
90
+ Each entry covers the violations of one rule in one file. The gate fails when a baselined file has more violations than its `count`, or a larger measure than its `measure`; the report then lists all of that file's violations for the rule. It also fails when an entry allows more than is left, including a file with no violations left and a rule that is off or unknown. The baseline covers violations at any level.
91
+
92
+ - `quality baseline write` records every error-level violation when there is no baseline yet. Once a baseline exists it refuses, unless `--rule <id>` names rules the baseline does not cover yet: that is how a rule is adopted later. It never raises an entry.
93
+ - `quality baseline prune` drops fixed debt and lowers entries to what is left. It never adds or raises an entry.
94
+
95
+ The file keeps the indentation it has, and its rules and files are sorted, so its diffs stay small.
96
+
97
+ ## Registry
98
+
99
+ ```json
100
+ [
101
+ {
102
+ "rule": "structure/max-lines",
103
+ "file": "src/generated/client.ts",
104
+ "reason": "Generated from the OpenAPI document; splitting it would mean forking the generator."
105
+ }
106
+ ]
107
+ ```
108
+
109
+ An entry covers a rule's violations in one file, for good, and must say why. With a `subject`, it covers only the violations with that subject. The registry applies before the baseline. An entry that covers nothing, or names a rule that is off or unknown, fails the gate until it is removed.
110
+
111
+ ## Command line
112
+
113
+ ```text
114
+ quality lint [--config <file>] [--warnings summary|all]
115
+ quality baseline write [--config <file>] [--rule <id>]...
116
+ quality baseline prune [--config <file>]
117
+ ```
118
+
119
+ `quality` alone runs `lint`. The report groups violations by rule and states each rule's description once; warnings are summarized per rule with the files that have the most, and `--warnings all` lists each one.
120
+
121
+ | Exit code | Meaning |
122
+ | --- | --- |
123
+ | 0 | Passed. Warnings may remain. |
124
+ | 1 | Failed: an uncovered error-level violation, a baselined file that got worse, or a stale baseline or registry entry. |
125
+ | 2 | Could not run: no or invalid config, an invalid baseline or registry, a missing source, a rule that threw, or a usage error. |
126
+
127
+ ## Validation
128
+
129
+ `pnpm ready` checks formatting, both TypeScript compilers, the rule, config, discovery, registry, baseline and report behavior, the command line against seeded repositories, and an installed tarball consumer that type-checks a config and runs the `quality` bin through a baseline cycle.
@@ -0,0 +1,26 @@
1
+ import type { Level } from "../config.ts";
2
+ import { type Violation } from "../engine/violation.ts";
3
+ import type { BaselineEntry } from "./format.ts";
4
+ export interface Tally {
5
+ readonly count: number;
6
+ readonly measure: number | undefined;
7
+ }
8
+ export interface Regression {
9
+ readonly entry: BaselineEntry;
10
+ readonly tally: Tally;
11
+ }
12
+ export interface StaleBaselineEntry {
13
+ readonly entry: BaselineEntry;
14
+ readonly problem: string;
15
+ }
16
+ export interface BaselineCheck {
17
+ readonly kept: ReadonlyArray<Violation>;
18
+ readonly regressions: ReadonlyArray<Regression>;
19
+ readonly stale: ReadonlyArray<StaleBaselineEntry>;
20
+ readonly baselined: number;
21
+ }
22
+ export declare const tallyOf: (violations: ReadonlyArray<Violation>) => Tally;
23
+ export declare const describeTally: (tally: Tally) => string;
24
+ export declare const describeEntry: (entry: BaselineEntry) => string;
25
+ export declare const applyBaseline: (violations: ReadonlyArray<Violation>, entries: ReadonlyArray<BaselineEntry>, levels: ReadonlyMap<string, Level>) => BaselineCheck;
26
+ //# sourceMappingURL=compare.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../src/baseline/compare.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAC5F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,WAAW,KAAK;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IACxC,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IAChD,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IAClD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC3B;AAOD,eAAO,MAAM,OAAO,GAAI,YAAY,aAAa,CAAC,SAAS,CAAC,KAAG,KAG9D,CAAC;AAaF,eAAO,MAAM,aAAa,GAAI,OAAO,KAAK,KAAG,MACgF,CAAC;AAE9H,eAAO,MAAM,aAAa,GAAI,OAAO,aAAa,KAAG,MAAuE,CAAC;AAE7H,eAAO,MAAM,aAAa,GACzB,YAAY,aAAa,CAAC,SAAS,CAAC,EACpC,SAAS,aAAa,CAAC,aAAa,CAAC,EACrC,QAAQ,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,KAChC,aAwBF,CAAC"}
@@ -0,0 +1,42 @@
1
+ import { groupBy, keyOf, unusedEntryProblem } from "../engine/violation.js";
2
+ const measures = (violations) => violations.flatMap((violation) => (violation.measure === undefined ? [] : [violation.measure]));
3
+ export const tallyOf = (violations) => {
4
+ const found = measures(violations);
5
+ return { count: violations.length, measure: found.length === 0 ? undefined : Math.max(...found) };
6
+ };
7
+ const measureTrend = (entry, tally) => entry.measure === undefined || tally.measure === undefined ? 0 : Math.sign(tally.measure - entry.measure);
8
+ const verdictOf = (entry, tally) => {
9
+ const trend = measureTrend(entry, tally);
10
+ if (tally.count > entry.count || trend > 0) {
11
+ return { _tag: "Regressed" };
12
+ }
13
+ return { _tag: "Covered", loose: tally.count < entry.count || trend < 0 };
14
+ };
15
+ export const describeTally = (tally) => `${tally.count} violation${tally.count === 1 ? "" : "s"}${tally.measure === undefined ? "" : ` measuring ${tally.measure}`}`;
16
+ export const describeEntry = (entry) => describeTally({ count: entry.count, measure: entry.measure });
17
+ export const applyBaseline = (violations, entries, levels) => {
18
+ const groups = groupBy(violations, (violation) => keyOf(violation.rule, violation.file));
19
+ const covered = new Set();
20
+ const regressions = [];
21
+ const stale = [];
22
+ for (const entry of entries) {
23
+ const group = groups.get(keyOf(entry.rule, entry.file)) ?? [];
24
+ if (group.length === 0) {
25
+ stale.push({ entry, problem: unusedEntryProblem(levels, entry.rule, "has no violations left") });
26
+ continue;
27
+ }
28
+ const tally = tallyOf(group);
29
+ const verdict = verdictOf(entry, tally);
30
+ if (verdict._tag === "Regressed") {
31
+ regressions.push({ entry, tally });
32
+ continue;
33
+ }
34
+ covered.add(keyOf(entry.rule, entry.file));
35
+ if (verdict.loose) {
36
+ stale.push({ entry, problem: `allows more than is left: ${describeTally(tally)} against ${describeEntry(entry)} baselined` });
37
+ }
38
+ }
39
+ const kept = violations.filter((violation) => !covered.has(keyOf(violation.rule, violation.file)));
40
+ return { baselined: violations.length - kept.length, kept, regressions, stale };
41
+ };
42
+ //# sourceMappingURL=compare.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare.js","sourceRoot":"","sources":["../../src/baseline/compare.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { type Decoded } from "../decoded.ts";
2
+ export interface BaselineEntry {
3
+ readonly rule: string;
4
+ readonly file: string;
5
+ readonly count: number;
6
+ readonly measure?: number | undefined;
7
+ }
8
+ export declare const byRuleAndFile: (left: BaselineEntry, right: BaselineEntry) => number;
9
+ export declare const decodeBaseline: (raw: string | undefined) => Promise<Decoded<ReadonlyArray<BaselineEntry>>>;
10
+ export declare const encodeBaseline: (entries: ReadonlyArray<BaselineEntry>, indent: string) => string;
11
+ /** Keeps the indentation of the file being rewritten, so the repository formatter leaves it alone. */
12
+ export declare const indentOf: (raw: string | undefined) => string;
13
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/baseline/format.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,OAAO,EAAc,MAAM,eAAe,CAAC;AAWzD,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC;AAMD,eAAO,MAAM,aAAa,GAAI,MAAM,aAAa,EAAE,OAAO,aAAa,KAAG,MACH,CAAC;AAExE,eAAO,MAAM,cAAc,GAAU,KAAK,MAAM,GAAG,SAAS,KAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAY3G,CAAC;AAKF,eAAO,MAAM,cAAc,GAAI,SAAS,aAAa,CAAC,aAAa,CAAC,EAAE,QAAQ,MAAM,KAAG,MAMtF,CAAC;AAEF,sGAAsG;AACtG,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,GAAG,SAAS,KAAG,MAAqD,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { Schema } from "effect";
2
+ import { decodeWith } from "../decoded.js";
3
+ const Entry = Schema.Struct({
4
+ count: Schema.Int.check(Schema.isGreaterThan(0)),
5
+ measure: Schema.optionalKey(Schema.Finite),
6
+ });
7
+ const BaselineFile = Schema.Record(Schema.String, Schema.Record(Schema.String, Entry));
8
+ const standard = Schema.toStandardSchemaV1(Schema.fromJsonString(BaselineFile), { parseOptions: { errors: "all", onExcessProperty: "error" } });
9
+ const codepoints = (left, right) => (left < right ? -1 : Number(left > right));
10
+ export const byRuleAndFile = (left, right) => codepoints(left.rule, right.rule) || codepoints(left.file, right.file);
11
+ export const decodeBaseline = async (raw) => {
12
+ if (raw === undefined) {
13
+ return { _tag: "Valid", value: [] };
14
+ }
15
+ const decoded = await decodeWith(standard, raw);
16
+ if (decoded._tag === "Invalid") {
17
+ return decoded;
18
+ }
19
+ const entries = Object.entries(decoded.value).flatMap(([rule, files]) => Object.entries(files).map(([file, stored]) => ({ ...stored, file, rule })));
20
+ return { _tag: "Valid", value: entries };
21
+ };
22
+ const stored = (entry) => entry.measure === undefined ? { count: entry.count } : { count: entry.count, measure: entry.measure };
23
+ export const encodeBaseline = (entries, indent) => {
24
+ const rules = {};
25
+ for (const entry of [...entries].sort(byRuleAndFile)) {
26
+ rules[entry.rule] = { ...rules[entry.rule], [entry.file]: stored(entry) };
27
+ }
28
+ return `${JSON.stringify(rules, null, indent)}\n`;
29
+ };
30
+ /** Keeps the indentation of the file being rewritten, so the repository formatter leaves it alone. */
31
+ export const indentOf = (raw) => /^([ \t]+)\S/m.exec(raw ?? "")?.[1] ?? "\t";
32
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/baseline/format.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { Level } from "../config.ts";
2
+ import { type Violation } from "../engine/violation.ts";
3
+ import type { BaselineEntry } from "./format.ts";
4
+ export type Adoption = {
5
+ readonly _tag: "Refused";
6
+ readonly reasons: ReadonlyArray<string>;
7
+ } | {
8
+ readonly _tag: "Adopted";
9
+ readonly entries: ReadonlyArray<BaselineEntry>;
10
+ readonly added: number;
11
+ };
12
+ export interface Pruned {
13
+ readonly entries: ReadonlyArray<BaselineEntry>;
14
+ readonly removed: number;
15
+ readonly lowered: number;
16
+ }
17
+ /**
18
+ * Records the error-level violations of rules being adopted. Without a baseline every rule is adopted at once; afterwards a
19
+ * rule is adopted by name, once, so a baseline never grows for a rule it already covers.
20
+ */
21
+ export declare const adopt: (existing: ReadonlyArray<BaselineEntry> | undefined, rules: ReadonlyArray<string>, violations: ReadonlyArray<Violation>, levels: ReadonlyMap<string, Level>) => Adoption;
22
+ /** Drops fixed debt and lowers entries to what is left. It never adds an entry or raises one. */
23
+ export declare const prune: (existing: ReadonlyArray<BaselineEntry>, violations: ReadonlyArray<Violation>, levels: ReadonlyMap<string, Level>) => Pruned;
24
+ //# sourceMappingURL=update.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/baseline/update.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,MAAM,QAAQ,GACjB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAE,GACrE;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAExG,MAAM,WAAW,MAAM;IACtB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IAC/C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAyBD;;;GAGG;AACH,eAAO,MAAM,KAAK,GACjB,UAAU,aAAa,CAAC,aAAa,CAAC,GAAG,SAAS,EAClD,OAAO,aAAa,CAAC,MAAM,CAAC,EAC5B,YAAY,aAAa,CAAC,SAAS,CAAC,EACpC,QAAQ,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,KAChC,QAWF,CAAC;AAWF,iGAAiG;AACjG,eAAO,MAAM,KAAK,GAAI,UAAU,aAAa,CAAC,aAAa,CAAC,EAAE,YAAY,aAAa,CAAC,SAAS,CAAC,EAAE,QAAQ,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,KAAG,MAYxI,CAAC"}
@@ -0,0 +1,60 @@
1
+ import { groupBy, keyOf } from "../engine/violation.js";
2
+ import { tallyOf } from "./compare.js";
3
+ const record = (violations) => [...groupBy(violations, (violation) => keyOf(violation.rule, violation.file)).values()].flatMap((group) => {
4
+ const first = group[0];
5
+ if (first === undefined) {
6
+ return [];
7
+ }
8
+ const tally = tallyOf(group);
9
+ return [
10
+ tally.measure === undefined ? { count: tally.count, file: first.file, rule: first.rule } : { ...tally, file: first.file, rule: first.rule },
11
+ ];
12
+ });
13
+ const refusal = (id, levels, existing) => {
14
+ const level = levels.get(id);
15
+ if (level === undefined) {
16
+ return [`${id}: no built-in or local rule has this id.`];
17
+ }
18
+ if (level !== "error") {
19
+ return [`${id}: the rule is ${level}. Set it to error before baselining it.`];
20
+ }
21
+ return existing.some((entry) => entry.rule === id) ? [`${id}: already baselined. Its entries only shrink; fix new violations instead.`] : [];
22
+ };
23
+ /**
24
+ * Records the error-level violations of rules being adopted. Without a baseline every rule is adopted at once; afterwards a
25
+ * rule is adopted by name, once, so a baseline never grows for a rule it already covers.
26
+ */
27
+ export const adopt = (existing, rules, violations, levels) => {
28
+ if (existing !== undefined && rules.length === 0) {
29
+ return { _tag: "Refused", reasons: ["a baseline exists and only shrinks. Adopt a new rule with --rule <id>, or drop fixed debt with prune."] };
30
+ }
31
+ const reasons = rules.flatMap((id) => refusal(id, levels, existing ?? []));
32
+ if (reasons.length > 0) {
33
+ return { _tag: "Refused", reasons };
34
+ }
35
+ const adopted = violations.filter((violation) => violation.level === "error" && (rules.length === 0 || rules.includes(violation.rule)));
36
+ const added = record(adopted);
37
+ return { _tag: "Adopted", added: added.length, entries: [...(existing ?? []), ...added] };
38
+ };
39
+ const shrunk = (entry, current) => {
40
+ const count = Math.min(entry.count, current.count);
41
+ return entry.measure === undefined || current.measure === undefined
42
+ ? { count, file: entry.file, rule: entry.rule }
43
+ : { count, file: entry.file, measure: Math.min(entry.measure, current.measure), rule: entry.rule };
44
+ };
45
+ const unchanged = (left, right) => left.count === right.count && left.measure === right.measure;
46
+ /** Drops fixed debt and lowers entries to what is left. It never adds an entry or raises one. */
47
+ export const prune = (existing, violations, levels) => {
48
+ const current = new Map(record(violations).map((entry) => [keyOf(entry.rule, entry.file), entry]));
49
+ const kept = existing.flatMap((before) => {
50
+ const now = current.get(keyOf(before.rule, before.file));
51
+ const level = levels.get(before.rule);
52
+ return now === undefined || level === undefined || level === "off" ? [] : [{ after: shrunk(before, now), before }];
53
+ });
54
+ return {
55
+ entries: kept.map(({ after }) => after),
56
+ lowered: kept.filter(({ after, before }) => !unchanged(before, after)).length,
57
+ removed: existing.length - kept.length,
58
+ };
59
+ };
60
+ //# sourceMappingURL=update.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.js","sourceRoot":"","sources":["../../src/baseline/update.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { WarningDetail } from "../report/render.ts";
2
+ export type Command = {
3
+ readonly _tag: "Lint";
4
+ readonly config: string | undefined;
5
+ readonly warnings: WarningDetail;
6
+ } | {
7
+ readonly _tag: "BaselineWrite";
8
+ readonly config: string | undefined;
9
+ readonly rules: ReadonlyArray<string>;
10
+ } | {
11
+ readonly _tag: "BaselinePrune";
12
+ readonly config: string | undefined;
13
+ } | {
14
+ readonly _tag: "Help";
15
+ };
16
+ export type Parsed = {
17
+ readonly _tag: "Parsed";
18
+ readonly command: Command;
19
+ } | {
20
+ readonly _tag: "Usage";
21
+ readonly problem: string;
22
+ };
23
+ export declare const USAGE = "Usage:\n quality lint [--config <file>] [--warnings summary|all]\n quality baseline write [--config <file>] [--rule <id>]...\n quality baseline prune [--config <file>]\n\nlint Run every rule. Exits 1 on an error-level violation, a baseline regression or a stale entry.\nbaseline write Record current error-level violations. Creates the baseline, or adopts named rules into an existing one.\nbaseline prune Drop fixed debt and lower entries to what is left. Never adds or raises an entry.\n\n--config <file> Config file; its directory is the repository root. Defaults to ./quality.config.ts.\nExit codes: 0 passed, 1 failed the gate, 2 could not run.";
24
+ export declare const parseCommand: (args: ReadonlyArray<string>) => Parsed;
25
+ //# sourceMappingURL=args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,MAAM,OAAO,GAChB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAA;CAAE,GAChG;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAE,GAC9G;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GACvE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7B,MAAM,MAAM,MAAM,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnI,eAAO,MAAM,KAAK,+pBAUwC,CAAC;AAsC3D,eAAO,MAAM,YAAY,GAAI,MAAM,aAAa,CAAC,MAAM,CAAC,KAAG,MAoB1D,CAAC"}
@@ -0,0 +1,64 @@
1
+ import { parseArgs } from "node:util";
2
+ import { Result } from "effect";
3
+ export const USAGE = `Usage:
4
+ quality lint [--config <file>] [--warnings summary|all]
5
+ quality baseline write [--config <file>] [--rule <id>]...
6
+ quality baseline prune [--config <file>]
7
+
8
+ lint Run every rule. Exits 1 on an error-level violation, a baseline regression or a stale entry.
9
+ baseline write Record current error-level violations. Creates the baseline, or adopts named rules into an existing one.
10
+ baseline prune Drop fixed debt and lower entries to what is left. Never adds or raises an entry.
11
+
12
+ --config <file> Config file; its directory is the repository root. Defaults to ./quality.config.ts.
13
+ Exit codes: 0 passed, 1 failed the gate, 2 could not run.`;
14
+ const OPTIONS = {
15
+ config: { type: "string" },
16
+ help: { short: "h", type: "boolean" },
17
+ rule: { multiple: true, type: "string" },
18
+ warnings: { type: "string" },
19
+ };
20
+ const usage = (problem) => ({ _tag: "Usage", problem });
21
+ const parsed = (command) => ({ _tag: "Parsed", command });
22
+ const lint = (values) => {
23
+ if (values.rule !== undefined) {
24
+ return usage("--rule applies only to baseline write.");
25
+ }
26
+ const warnings = values.warnings ?? "summary";
27
+ return warnings === "summary" || warnings === "all"
28
+ ? parsed({ _tag: "Lint", config: values.config, warnings })
29
+ : usage(`--warnings takes summary or all, not "${warnings}".`);
30
+ };
31
+ const baseline = (action, values) => {
32
+ if (values.warnings !== undefined) {
33
+ return usage("--warnings applies only to lint.");
34
+ }
35
+ if (action === "write") {
36
+ return parsed({ _tag: "BaselineWrite", config: values.config, rules: values.rule ?? [] });
37
+ }
38
+ if (action === "prune" && values.rule === undefined) {
39
+ return parsed({ _tag: "BaselinePrune", config: values.config });
40
+ }
41
+ return usage(action === "prune" ? "--rule applies only to baseline write." : "baseline takes write or prune.");
42
+ };
43
+ export const parseCommand = (args) => {
44
+ const result = Result.try({
45
+ catch: (error) => (error instanceof Error ? error.message : String(error)),
46
+ try: () => parseArgs({ allowPositionals: true, args: [...args], options: OPTIONS, strict: true }),
47
+ });
48
+ if (Result.isFailure(result)) {
49
+ return usage(result.failure);
50
+ }
51
+ const { positionals, values } = result.success;
52
+ const [command = "lint", action, ...extra] = positionals;
53
+ if (values.help === true || command === "help") {
54
+ return parsed({ _tag: "Help" });
55
+ }
56
+ if (command === "lint" && action === undefined) {
57
+ return lint(values);
58
+ }
59
+ if (command === "baseline" && extra.length === 0) {
60
+ return baseline(action, values);
61
+ }
62
+ return usage(`unknown command: ${positionals.join(" ")}`);
63
+ };
64
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { Effect, type FileSystem } from "effect";
2
+ import { SetupFailure } from "../failure.ts";
3
+ export declare const writeBaseline: (cwd: string, config: string | undefined, rules: ReadonlyArray<string>) => Effect.Effect<void, SetupFailure, FileSystem.FileSystem>;
4
+ export declare const pruneBaseline: (cwd: string, config: string | undefined) => Effect.Effect<void, SetupFailure, FileSystem.FileSystem>;
5
+ //# sourceMappingURL=baseline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseline.d.ts","sourceRoot":"","sources":["../../src/cli/baseline.ts"],"names":[],"mappings":"AACA,OAAO,EAAW,MAAM,EAAE,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC;AAK1D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAY7C,eAAO,MAAM,aAAa,GACzB,KAAK,MAAM,EACX,QAAQ,MAAM,GAAG,SAAS,EAC1B,OAAO,aAAa,CAAC,MAAM,CAAC,KAC1B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,EAAE,UAAU,CAAC,UAAU,CAavD,CAAC;AAEJ,eAAO,MAAM,aAAa,GAAI,KAAK,MAAM,EAAE,QAAQ,MAAM,GAAG,SAAS,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,EAAE,UAAU,CAAC,UAAU,CAc5H,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { join } from "node:path";
2
+ import { Console, Effect } from "effect";
3
+ import { encodeBaseline, indentOf } from "../baseline/format.js";
4
+ import { adopt, prune } from "../baseline/update.js";
5
+ import { openSession } from "../engine/session.js";
6
+ import { applyRegistry } from "../exceptions/registry.js";
7
+ import { SetupFailure } from "../failure.js";
8
+ import { writeText } from "../inventory/filesystem.js";
9
+ import { plural } from "../report/plural.js";
10
+ const save = (session, entries) => Effect.mapError(writeText(join(session.config.root, session.config.baseline), encodeBaseline(entries, indentOf(session.baseline.raw))), (failure) => new SetupFailure({ message: failure.message }));
11
+ const unregistered = (session) => applyRegistry(session.violations, session.registry, session.config.levels).kept;
12
+ export const writeBaseline = (cwd, config, rules) => Effect.gen(function* () {
13
+ const session = yield* openSession(cwd, config);
14
+ const existing = session.baseline.raw === undefined ? undefined : session.baseline.entries;
15
+ const adoption = adopt(existing, rules, unregistered(session), session.config.levels);
16
+ if (adoption._tag === "Refused") {
17
+ return yield* new SetupFailure({ message: `baseline write refused:\n${adoption.reasons.map((reason) => ` - ${reason}`).join("\n")}` });
18
+ }
19
+ if (adoption.added === 0) {
20
+ return yield* Console.log(`quality: no error-level violations to record; ${session.config.baseline} is unchanged.`);
21
+ }
22
+ yield* save(session, adoption.entries);
23
+ yield* Console.log(`quality: recorded ${plural(adoption.added, "entry", "entries")} in ${session.config.baseline}.`);
24
+ });
25
+ export const pruneBaseline = (cwd, config) => Effect.gen(function* () {
26
+ const session = yield* openSession(cwd, config);
27
+ if (session.baseline.raw === undefined) {
28
+ return yield* Console.log(`quality: no baseline at ${session.config.baseline}; nothing to prune.`);
29
+ }
30
+ const pruned = prune(session.baseline.entries, unregistered(session), session.config.levels);
31
+ if (pruned.removed === 0 && pruned.lowered === 0) {
32
+ return yield* Console.log(`quality: ${session.config.baseline} is current.`);
33
+ }
34
+ yield* save(session, pruned.entries);
35
+ yield* Console.log(`quality: removed ${plural(pruned.removed, "entry", "entries")} and lowered ${plural(pruned.lowered, "entry", "entries")} in ${session.config.baseline}.`);
36
+ });
37
+ //# sourceMappingURL=baseline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseline.js","sourceRoot":"","sources":["../../src/cli/baseline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { Effect, type FileSystem } from "effect";
2
+ import { GateFailed, type SetupFailure } from "../failure.ts";
3
+ import { type WarningDetail } from "../report/render.ts";
4
+ export declare const lint: (cwd: string, config: string | undefined, warnings: WarningDetail) => Effect.Effect<void, SetupFailure | GateFailed, FileSystem.FileSystem>;
5
+ //# sourceMappingURL=lint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../../src/cli/lint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,MAAM,EAAE,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC;AAG1D,OAAO,EAAE,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAU,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEjE,eAAO,MAAM,IAAI,GAChB,KAAK,MAAM,EACX,QAAQ,MAAM,GAAG,SAAS,EAC1B,UAAU,aAAa,KACrB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,UAAU,EAAE,UAAU,CAAC,UAAU,CAgBpE,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { Console, Effect } from "effect";
2
+ import { evaluate, passes } from "../engine/evaluate.js";
3
+ import { openSession } from "../engine/session.js";
4
+ import { GateFailed } from "../failure.js";
5
+ import { render } from "../report/render.js";
6
+ export const lint = (cwd, config, warnings) => Effect.gen(function* () {
7
+ const session = yield* openSession(cwd, config);
8
+ const outcome = evaluate(session.violations, session.registry, session.baseline.entries, session.config.levels);
9
+ yield* Console.log(render(outcome, {
10
+ baseline: session.config.baseline,
11
+ checked: session.inventory.sources.length,
12
+ descriptions: new Map(session.config.active.map((rule) => [rule.id, rule.description])),
13
+ registry: session.config.registry,
14
+ warnings,
15
+ }));
16
+ if (!passes(outcome)) {
17
+ return yield* new GateFailed();
18
+ }
19
+ });
20
+ //# sourceMappingURL=lint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint.js","sourceRoot":"","sources":["../../src/cli/lint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAmB,MAAM,QAAQ,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAqB,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAsB,MAAM,qBAAqB,CAAC;AAEjE,MAAM,CAAC,MAAM,IAAI,GAAG,CACnB,GAAW,EACX,MAA0B,EAC1B,QAAuB,EACiD,EAAE,CAC1E,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACnB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAChH,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CACjB,MAAM,CAAC,OAAO,EAAE;QACf,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ;QACjC,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM;QACzC,YAAY,EAAE,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;QACvF,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ;QACjC,QAAQ;KACR,CAAC,CACF,CAAC;IACF,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QACtB,OAAO,KAAK,CAAC,CAAC,IAAI,UAAU,EAAE,CAAC;IAChC,CAAC;AACF,CAAC,CAAC,CAAC"}