@avi2dg/checks 0.26.0 → 0.28.0

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 (54) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +7 -6
  3. package/dist/data-shape/index.js +428 -0
  4. package/dist/templates/reference.md +1 -1
  5. package/docs/configs/commit-messages.md +1 -1
  6. package/docs/configs/dependency-rules.md +7 -1
  7. package/docs/configs/effect-rules.md +10 -0
  8. package/docs/configs/native-settings.md +8 -30
  9. package/docs/configs/typescript-rules.md +43 -1
  10. package/docs/design.md +232 -104
  11. package/docs/gates/checks-advisories.md +128 -0
  12. package/docs/gates/checks-changelog.md +8 -5
  13. package/docs/gates/checks-ci-wiring.md +2 -2
  14. package/docs/gates/checks-comment-gate.md +3 -4
  15. package/docs/gates/checks-commit-identity.md +3 -3
  16. package/docs/gates/checks-docs.md +56 -36
  17. package/docs/gates/checks-exports.md +4 -4
  18. package/docs/gates/checks-flake.md +3 -3
  19. package/docs/gates/checks-lint-coverage.md +17 -9
  20. package/docs/gates/checks-lint.md +9 -4
  21. package/docs/gates/checks-mutation-compare.md +3 -3
  22. package/docs/gates/checks-quarantine-clock.md +3 -5
  23. package/docs/gates/checks-release-notes.md +4 -4
  24. package/docs/gates/checks-release-report.md +3 -3
  25. package/docs/gates/checks-repetition.md +2 -1
  26. package/docs/gates/checks-subsumed-tests.md +3 -3
  27. package/docs/gates/checks-suppressions-ratchet.md +3 -4
  28. package/docs/gates/checks-test-layout.md +4 -4
  29. package/docs/gates/checks-test.md +3 -2
  30. package/docs/gates/checks-unused.md +4 -4
  31. package/docs/gates/checks-vendor.md +3 -3
  32. package/oxlintrc.json +22 -2
  33. package/package.json +16 -6
  34. package/src/complexity/exports.ts +8 -16
  35. package/src/complexity/knip.ts +1 -1
  36. package/src/core/gates.ts +3 -0
  37. package/src/core/git.ts +10 -0
  38. package/src/delivery/ci-wiring.ts +1 -1
  39. package/src/dependencies/advisories.ts +115 -0
  40. package/src/dependencies/advisory-rules.ts +206 -0
  41. package/src/dependencies/cache-root.ts +15 -0
  42. package/src/dependencies/osv-scanner.ts +176 -0
  43. package/src/dependencies/vendor.ts +1 -10
  44. package/src/docs/doc-names.ts +128 -0
  45. package/src/docs/doc-templates.ts +1 -1
  46. package/src/docs/docs.ts +17 -14
  47. package/src/docs/prose-matchers.ts +11 -1
  48. package/src/quality/lint-coverage.sh +35 -2
  49. package/src/quality/presets/effect.language-service.json +3 -1
  50. package/src/testing/mutation-compare.ts +51 -45
  51. package/src/testing/quarantine-clock.ts +2 -11
  52. package/src/testing/test-layout.ts +1 -1
  53. package/ts-reset.d.ts +2 -0
  54. package/tsconfig.effect.json +3 -0
@@ -2,27 +2,43 @@
2
2
  import { Console, Effect, FileSystem, Schema } from "effect";
3
3
  import { runMain, Usage } from "../core/main.ts";
4
4
 
5
- export type Location = {
6
- readonly start: { readonly line: number; readonly column: number };
7
- readonly end: { readonly line: number; readonly column: number };
8
- };
5
+ const PositionSchema = Schema.Struct({ line: Schema.Finite, column: Schema.Finite });
6
+ const LocationSchema = Schema.Struct({ start: PositionSchema, end: PositionSchema });
7
+
8
+ export type Location = typeof LocationSchema.Type;
9
+
10
+ const MutantStatusSchema = Schema.Literals([
11
+ "Killed",
12
+ "Survived",
13
+ "Timeout",
14
+ "NoCoverage",
15
+ "Ignored",
16
+ "CompileError",
17
+ "RuntimeError",
18
+ "Pending",
19
+ ]);
20
+
21
+ export type MutantStatus = typeof MutantStatusSchema.Type;
22
+
23
+ const MutantSchema = Schema.Struct({
24
+ status: MutantStatusSchema,
25
+ mutatorName: Schema.String,
26
+ replacement: Schema.String,
27
+ killedBy: Schema.optionalKey(Schema.Array(Schema.String)),
28
+ location: LocationSchema,
29
+ });
9
30
 
10
- export type Mutant = {
11
- readonly status: string;
12
- readonly mutatorName: string;
13
- readonly replacement: string;
14
- readonly killedBy?: readonly string[];
15
- readonly location: Location;
16
- };
31
+ export type Mutant = typeof MutantSchema.Type;
17
32
 
18
- export type ReportFile = {
19
- readonly source: string;
20
- readonly mutants: readonly Mutant[];
21
- };
33
+ const ReportFileSchema = Schema.Struct({ source: Schema.String, mutants: Schema.Array(MutantSchema) });
22
34
 
23
- export type TestFile = {
24
- readonly tests: readonly { readonly id: string; readonly name: string }[];
25
- };
35
+ export type ReportFile = typeof ReportFileSchema.Type;
36
+
37
+ const TestFileSchema = Schema.Struct({
38
+ tests: Schema.Array(Schema.Struct({ id: Schema.String, name: Schema.String })),
39
+ });
40
+
41
+ export type TestFile = typeof TestFileSchema.Type;
26
42
 
27
43
  export type KillRun = {
28
44
  readonly files: ReadonlyMap<string, ReportFile>;
@@ -35,8 +51,8 @@ export type MutantChange = {
35
51
  readonly location: Location;
36
52
  readonly mutatorName: string;
37
53
  readonly replacement: string;
38
- readonly from: string;
39
- readonly to: string;
54
+ readonly from: MutantStatus;
55
+ readonly to: MutantStatus;
40
56
  };
41
57
 
42
58
  export type UnmatchedMutant = {
@@ -44,7 +60,7 @@ export type UnmatchedMutant = {
44
60
  readonly location: Location;
45
61
  readonly mutatorName: string;
46
62
  readonly replacement: string;
47
- readonly status: string;
63
+ readonly status: MutantStatus;
48
64
  };
49
65
 
50
66
  export type Comparison = {
@@ -65,35 +81,25 @@ export class ReportError extends Schema.TaggedError<ReportError>()("ReportError"
65
81
  message: Schema.String,
66
82
  }) {}
67
83
 
68
- const DETECTED = new Set(["Killed", "Timeout"]);
69
- const UNDETECTED = new Set(["Survived", "NoCoverage"]);
70
- const LEAVES_SCORE = new Set(["CompileError", "RuntimeError", "Ignored", "Pending"]);
84
+ const OUTCOME = {
85
+ Killed: "detected",
86
+ Timeout: "detected",
87
+ Survived: "undetected",
88
+ NoCoverage: "undetected",
89
+ CompileError: "leaves-score",
90
+ RuntimeError: "leaves-score",
91
+ Ignored: "leaves-score",
92
+ Pending: "leaves-score",
93
+ } as const satisfies Record<MutantStatus, "detected" | "undetected" | "leaves-score">;
71
94
  const USAGE = "usage: mutation-compare.ts [--advisory] <base-report> <head-report>";
72
95
 
73
- const Files = Schema.Record(
74
- Schema.String,
75
- Schema.Struct({
76
- source: Schema.String,
77
- mutants: Schema.Array(
78
- Schema.Struct({
79
- status: Schema.String,
80
- mutatorName: Schema.String,
81
- replacement: Schema.String,
82
- killedBy: Schema.optionalKey(Schema.Array(Schema.String)),
83
- location: Schema.Struct({
84
- start: Schema.Struct({ line: Schema.Finite, column: Schema.Finite }),
85
- end: Schema.Struct({ line: Schema.Finite, column: Schema.Finite }),
86
- }),
87
- }),
88
- ),
89
- }),
90
- );
96
+ const Files = Schema.Record(Schema.String, ReportFileSchema);
91
97
  const decodeReport = Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Struct({ files: Files })));
92
98
  const decodeKillRun = Schema.decodeUnknownEffect(
93
99
  Schema.fromJsonString(
94
100
  Schema.Struct({
95
101
  files: Files,
96
- testFiles: Schema.Record(Schema.String, Schema.Struct({ tests: Schema.Array(Schema.Struct({ id: Schema.String, name: Schema.String })) })),
102
+ testFiles: Schema.Record(Schema.String, TestFileSchema),
97
103
  config: Schema.optionalKey(Schema.Struct({ disableBail: Schema.optionalKey(Schema.Boolean) })),
98
104
  }),
99
105
  ),
@@ -211,9 +217,9 @@ function judgeMatch(path: string, mutant: UnmatchedMutant, counterpart: Unmatche
211
217
  from: mutant.status,
212
218
  to: counterpart.status,
213
219
  };
214
- if (mutant.status !== counterpart.status && (LEAVES_SCORE.has(mutant.status) || LEAVES_SCORE.has(counterpart.status))) {
220
+ if (mutant.status !== counterpart.status && (OUTCOME[mutant.status] === "leaves-score" || OUTCOME[counterpart.status] === "leaves-score")) {
215
221
  findings.moves.push(change);
216
- } else if (DETECTED.has(mutant.status) && UNDETECTED.has(counterpart.status)) {
222
+ } else if (OUTCOME[mutant.status] === "detected" && OUTCOME[counterpart.status] === "undetected") {
217
223
  findings.regressions.push(change);
218
224
  }
219
225
  }
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  import { Console, Effect, Schema } from "effect";
3
- import { commitOf, git, isShallowBoundary, refArgs } from "../core/git.ts";
3
+ import { commitOf, git, isShallowBoundary, refArgs, writtenAt } from "../core/git.ts";
4
4
  import { runMain } from "../core/main.ts";
5
5
  import { TEST_FILE } from "./test-layout.ts";
6
6
 
@@ -125,19 +125,10 @@ const entryAt = Effect.fn("entryAt")(function* (root: string, head: string, file
125
125
  return { file, at: entry.at, day: entry.day };
126
126
  });
127
127
 
128
- const headAt = Effect.fn("headAt")(function* (root: string, head: string) {
129
- const shown = yield* git(["show", "-s", "--format=%at %ct", head], root);
130
- const dates = shown.trim().split(" ").map(Number);
131
- if (dates.length !== 2 || !dates.every(Number.isInteger)) {
132
- return yield* new QuarantineError({ message: `cannot read when ${head} was written` });
133
- }
134
- return Math.max(...dates);
135
- });
136
-
137
128
  export const runHead = Effect.fn("runHead")(function* (root: string, head: string) {
138
129
  const files = yield* filesAt(root, head);
139
130
  const entries = yield* Effect.forEach(files, (file) => entryAt(root, head, file));
140
- return { checked: files.length, overdue: overdueOf(entries, yield* headAt(root, head)) } satisfies ClockResult;
131
+ return { checked: files.length, overdue: overdueOf(entries, yield* writtenAt(head, root)) } satisfies ClockResult;
141
132
  });
142
133
 
143
134
  const clock = Effect.gen(function* () {
@@ -177,7 +177,7 @@ function callUse(node: Record<string, unknown>): string | undefined {
177
177
  const callee = node["callee"];
178
178
  const calleeName = identifierName(callee);
179
179
  if (calleeName !== undefined && BANNED_GLOBAL_CALLS.includes(calleeName)) return `calls ${calleeName}`;
180
- const argument = Array.isArray(node["arguments"]) ? node["arguments"][0] : undefined;
180
+ const argument: unknown = Array.isArray(node["arguments"]) ? node["arguments"][0] : undefined;
181
181
  const specifier = isRecord(argument) ? stringValue(argument["expression"]) : undefined;
182
182
  const banned = specifier === undefined ? undefined : bannedModule(specifier);
183
183
  if (banned === undefined) return undefined;
package/ts-reset.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ /// <reference types="@total-typescript/ts-reset/is-array" />
2
+ /// <reference types="@total-typescript/ts-reset/json-parse" />
@@ -1,6 +1,9 @@
1
1
  {
2
+ "files": ["./ts-reset.d.ts"],
3
+ "include": ["${configDir}/**/*", "./ts-reset.d.ts"],
2
4
  "compilerOptions": {
3
5
  "erasableSyntaxOnly": true,
6
+ "exactOptionalPropertyTypes": true,
4
7
  "plugins": [
5
8
  {
6
9
  "name": "@effect/language-service",