@gobing-ai/ts-rule-engine 0.2.6 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +563 -1
- package/dist/config/extensions.d.ts +48 -0
- package/dist/config/extensions.d.ts.map +1 -0
- package/dist/config/extensions.js +67 -0
- package/dist/config/loader.d.ts +20 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +52 -26
- package/dist/engine.d.ts +26 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +79 -0
- package/dist/evaluators/exit-code-evaluator.d.ts +1 -1
- package/dist/evaluators/exit-code-evaluator.d.ts.map +1 -1
- package/dist/evaluators/exit-code-evaluator.js +22 -9
- package/dist/evaluators/forbidden-import-evaluator.d.ts +16 -3
- package/dist/evaluators/forbidden-import-evaluator.d.ts.map +1 -1
- package/dist/evaluators/forbidden-import-evaluator.js +71 -6
- package/dist/evaluators/import-boundary-evaluator.d.ts +19 -0
- package/dist/evaluators/import-boundary-evaluator.d.ts.map +1 -0
- package/dist/evaluators/import-boundary-evaluator.js +85 -0
- package/dist/evaluators/path-evaluator.d.ts +15 -2
- package/dist/evaluators/path-evaluator.d.ts.map +1 -1
- package/dist/evaluators/path-evaluator.js +49 -3
- package/dist/evaluators/regex-evaluator.d.ts.map +1 -1
- package/dist/evaluators/regex-evaluator.js +43 -8
- package/dist/evaluators/schema-artifact-evaluator.d.ts +21 -0
- package/dist/evaluators/schema-artifact-evaluator.d.ts.map +1 -0
- package/dist/evaluators/schema-artifact-evaluator.js +102 -0
- package/dist/evaluators/secrets-scanner-evaluator.d.ts +13 -2
- package/dist/evaluators/secrets-scanner-evaluator.d.ts.map +1 -1
- package/dist/evaluators/secrets-scanner-evaluator.js +72 -9
- package/dist/evaluators/sg-evaluator.d.ts +19 -0
- package/dist/evaluators/sg-evaluator.d.ts.map +1 -0
- package/dist/evaluators/sg-evaluator.js +112 -0
- package/dist/evaluators/test-location-evaluator.d.ts +14 -1
- package/dist/evaluators/test-location-evaluator.d.ts.map +1 -1
- package/dist/evaluators/test-location-evaluator.js +42 -22
- package/dist/evaluators/tsdoc-export-evaluator.js +19 -5
- package/dist/fixers/fixers.d.ts +86 -0
- package/dist/fixers/fixers.d.ts.map +1 -0
- package/dist/fixers/fixers.js +230 -0
- package/dist/fixers/test-stub-fixer.d.ts +49 -0
- package/dist/fixers/test-stub-fixer.d.ts.map +1 -0
- package/dist/fixers/test-stub-fixer.js +91 -0
- package/dist/host/builtins.d.ts.map +1 -1
- package/dist/host/builtins.js +12 -1
- package/dist/host/rule-engine-host.d.ts +3 -0
- package/dist/host/rule-engine-host.d.ts.map +1 -1
- package/dist/host/rule-engine-host.js +3 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/resolvers/test-path-resolver.d.ts +72 -0
- package/dist/resolvers/test-path-resolver.d.ts.map +1 -0
- package/dist/resolvers/test-path-resolver.js +112 -0
- package/dist/types.d.ts +36 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +10 -0
- package/package.json +4 -3
- package/schemas/preset.schema.json +40 -0
- package/schemas/rule-file.schema.json +49 -0
- package/src/config/extensions.ts +115 -0
- package/src/config/loader.ts +81 -25
- package/src/engine.ts +99 -2
- package/src/evaluators/exit-code-evaluator.ts +27 -9
- package/src/evaluators/forbidden-import-evaluator.ts +101 -7
- package/src/evaluators/import-boundary-evaluator.ts +135 -0
- package/src/evaluators/path-evaluator.ts +66 -3
- package/src/evaluators/regex-evaluator.ts +53 -12
- package/src/evaluators/schema-artifact-evaluator.ts +134 -0
- package/src/evaluators/secrets-scanner-evaluator.ts +89 -11
- package/src/evaluators/sg-evaluator.ts +133 -0
- package/src/evaluators/test-location-evaluator.ts +47 -35
- package/src/evaluators/tsdoc-export-evaluator.ts +19 -5
- package/src/fixers/fixers.ts +294 -0
- package/src/fixers/test-stub-fixer.ts +118 -0
- package/src/host/builtins.ts +17 -1
- package/src/host/rule-engine-host.ts +4 -0
- package/src/index.ts +4 -0
- package/src/resolvers/test-path-resolver.ts +133 -0
- package/src/types.ts +40 -0
|
@@ -8,12 +8,18 @@ import { discoverFiles, matchesGlob } from './file-utils.js';
|
|
|
8
8
|
* - `expected`: glob the test files must match (required)
|
|
9
9
|
* - `forbid`: globs where tests must not live (e.g. `**\/__tests__/**`)
|
|
10
10
|
* - `requireCorrespondingTest`: when true, flags source files (from `rule.include`)
|
|
11
|
-
* that lack a test at the
|
|
11
|
+
* that lack a test at the resolver's conventional path
|
|
12
|
+
* - `resolver`: language resolver name (`typescript` default, or `python`/`go`/`rust`)
|
|
12
13
|
*
|
|
13
14
|
* Discovery walks the workdir and applies `**` globs precisely, so it stays
|
|
14
15
|
* self-contained (no `rg --files` shell-out).
|
|
15
16
|
*/
|
|
16
17
|
export class TestLocationEvaluator {
|
|
18
|
+
resolvers;
|
|
19
|
+
/** Optional resolver registry; when absent, the TypeScript convention is used. */
|
|
20
|
+
constructor(resolvers) {
|
|
21
|
+
this.resolvers = resolvers;
|
|
22
|
+
}
|
|
17
23
|
/** Evaluate test-file placement and optional coverage of source files. */
|
|
18
24
|
async evaluate(rule, context) {
|
|
19
25
|
const config = (rule.evaluator.config ?? {});
|
|
@@ -33,18 +39,19 @@ export class TestLocationEvaluator {
|
|
|
33
39
|
continue;
|
|
34
40
|
const violated = forbid.find((pattern) => matchesGlob(file, pattern));
|
|
35
41
|
if (violated !== undefined) {
|
|
36
|
-
findings.push(createFinding(rule, `
|
|
42
|
+
findings.push(createFinding(rule, `test file in forbidden location (matches "${violated}")`, file, {
|
|
37
43
|
code: 'test-location:forbidden',
|
|
38
44
|
}));
|
|
39
45
|
continue;
|
|
40
46
|
}
|
|
41
47
|
if (!matchesGlob(file, expected)) {
|
|
42
|
-
findings.push(createFinding(rule, `
|
|
48
|
+
findings.push(createFinding(rule, `test file outside expected location (expected "${expected}")`, file, {
|
|
43
49
|
code: 'test-location:unexpected',
|
|
44
50
|
}));
|
|
45
51
|
}
|
|
46
52
|
}
|
|
47
53
|
if (config.requireCorrespondingTest) {
|
|
54
|
+
const resolver = this.selectResolver(config.resolver);
|
|
48
55
|
const srcPatterns = rule.include ?? ['**/*.ts', '**/*.tsx'];
|
|
49
56
|
const testSet = new Set(testFiles);
|
|
50
57
|
for (const srcFile of allFiles) {
|
|
@@ -52,11 +59,11 @@ export class TestLocationEvaluator {
|
|
|
52
59
|
continue;
|
|
53
60
|
if (exclude.some((pattern) => matchesGlob(srcFile, pattern)))
|
|
54
61
|
continue;
|
|
55
|
-
const testPath = resolveTestPath(srcFile);
|
|
62
|
+
const testPath = resolver.resolveTestPath(srcFile);
|
|
56
63
|
if (testPath === srcFile)
|
|
57
64
|
continue;
|
|
58
65
|
if (!testSet.has(testPath)) {
|
|
59
|
-
findings.push(createFinding(rule, `
|
|
66
|
+
findings.push(createFinding(rule, `no corresponding test → ${testPath}`, srcFile, {
|
|
60
67
|
code: 'test-location:missing',
|
|
61
68
|
}));
|
|
62
69
|
}
|
|
@@ -64,22 +71,35 @@ export class TestLocationEvaluator {
|
|
|
64
71
|
}
|
|
65
72
|
return { findings, fixes: [] };
|
|
66
73
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return `${pkg}/tests/${rel}`;
|
|
74
|
+
/**
|
|
75
|
+
* Pick the resolver named in config (default `typescript`).
|
|
76
|
+
*
|
|
77
|
+
* Falls back to the built-in TypeScript convention when no registry was
|
|
78
|
+
* injected; throws if a named resolver is requested but not registered.
|
|
79
|
+
*/
|
|
80
|
+
selectResolver(name = 'typescript') {
|
|
81
|
+
if (this.resolvers === undefined) {
|
|
82
|
+
if (name !== 'typescript') {
|
|
83
|
+
throw new Error(`test-location resolver "${name}" requested but no resolver registry is available`);
|
|
84
|
+
}
|
|
85
|
+
return TYPESCRIPT_FALLBACK;
|
|
86
|
+
}
|
|
87
|
+
return this.resolvers.get(name);
|
|
82
88
|
}
|
|
83
|
-
const withoutExt = srcRelPath.replace(/\.(ts|tsx|js|jsx)$/, '');
|
|
84
|
-
return `tests/${withoutExt.replace(/^src\//, '')}.test.ts`;
|
|
85
89
|
}
|
|
90
|
+
/** Built-in TypeScript convention used when no resolver registry is injected. */
|
|
91
|
+
const TYPESCRIPT_FALLBACK = {
|
|
92
|
+
name: 'typescript',
|
|
93
|
+
resolveTestPath(srcRelPath) {
|
|
94
|
+
if (srcRelPath.includes('.test.') || srcRelPath.includes('.spec.'))
|
|
95
|
+
return srcRelPath;
|
|
96
|
+
const srcIdx = srcRelPath.indexOf('/src/');
|
|
97
|
+
if (srcIdx !== -1) {
|
|
98
|
+
const pkg = srcRelPath.slice(0, srcIdx);
|
|
99
|
+
const rel = srcRelPath.slice(srcIdx + '/src/'.length).replace(/\.(ts|tsx|js|jsx)$/, '.test.ts');
|
|
100
|
+
return `${pkg}/tests/${rel}`;
|
|
101
|
+
}
|
|
102
|
+
const withoutExt = srcRelPath.replace(/\.(ts|tsx|js|jsx)$/, '');
|
|
103
|
+
return `tests/${withoutExt.replace(/^src\//, '')}.test.ts`;
|
|
104
|
+
},
|
|
105
|
+
};
|
|
@@ -3,8 +3,8 @@ import { discoverFiles, matchesGlob, readWorkdirFile } from './file-utils.js';
|
|
|
3
3
|
/** Export kinds this evaluator can check for a preceding JSDoc block. */
|
|
4
4
|
const VALID_KINDS = ['function', 'class', 'type', 'const', 'enum', 'interface'];
|
|
5
5
|
const KIND_PATTERN = {
|
|
6
|
-
function: /^export\s+(?:async\s+)?function\s+([A-Za-z0-9_$]+)/,
|
|
7
|
-
class: /^export\s+(?:abstract\s+)?class\s+([A-Za-z0-9_$]+)/,
|
|
6
|
+
function: /^export\s+(?:default\s+)?(?:async\s+)?function\s*\*?\s+([A-Za-z0-9_$]+)/,
|
|
7
|
+
class: /^export\s+(?:default\s+)?(?:abstract\s+)?class\s+([A-Za-z0-9_$]+)/,
|
|
8
8
|
interface: /^export\s+interface\s+([A-Za-z0-9_$]+)/,
|
|
9
9
|
type: /^export\s+type\s+([A-Za-z0-9_$]+)/,
|
|
10
10
|
const: /^export\s+const\s+([A-Za-z0-9_$]+)/,
|
|
@@ -70,8 +70,22 @@ function findExports(lines, requested) {
|
|
|
70
70
|
}
|
|
71
71
|
return sites;
|
|
72
72
|
}
|
|
73
|
-
/**
|
|
73
|
+
/**
|
|
74
|
+
* True when a JSDoc block precedes a declaration.
|
|
75
|
+
*
|
|
76
|
+
* Skips decorator lines (`@Component(...)`) so a documented but decorated class
|
|
77
|
+
* is not falsely flagged, then checks whether the nearest preceding non-decorator
|
|
78
|
+
* line closes (or is) a JSDoc comment.
|
|
79
|
+
*/
|
|
74
80
|
function precededByJsdoc(lines, declarationLine) {
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
let cursor = declarationLine - 2; // zero-based line above the declaration
|
|
82
|
+
while (cursor >= 0) {
|
|
83
|
+
const prev = lines[cursor]?.trim() ?? '';
|
|
84
|
+
if (prev.startsWith('@')) {
|
|
85
|
+
cursor -= 1; // decorator — keep walking up
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
return prev.endsWith('*/') || prev.startsWith('/**');
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
77
91
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core fixer pipeline: provider contracts, built-in fixer implementations,
|
|
3
|
+
* and the byte-range fix application function.
|
|
4
|
+
*
|
|
5
|
+
* @module rule-engine/fixers
|
|
6
|
+
*/
|
|
7
|
+
import { NodeFileSystem, type ProcessExecutor } from '@gobing-ai/ts-runtime';
|
|
8
|
+
import type { CapabilityRegistry } from '../host/capability-registry';
|
|
9
|
+
import type { TestPathResolver } from '../resolvers/test-path-resolver';
|
|
10
|
+
import type { ConstraintFinding, ConstraintRule, Fix, FixMode, RuleContext } from '../types';
|
|
11
|
+
/** Numeric ordering for fix authority; higher means more write authority. */
|
|
12
|
+
export declare const FIX_MODE_RANK: Record<FixMode, number>;
|
|
13
|
+
/** Result of resolving effective fix authority for a rule. */
|
|
14
|
+
export interface EffectiveFix {
|
|
15
|
+
/** Effective mode after all downgrades. */
|
|
16
|
+
readonly mode: FixMode;
|
|
17
|
+
/** Optional replacement text configured by the rule author. */
|
|
18
|
+
readonly replacement?: string;
|
|
19
|
+
/** Optional provider-specific parameters. */
|
|
20
|
+
readonly params?: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
/** Input passed to a rule fixer provider. */
|
|
23
|
+
export interface RuleFixerInput {
|
|
24
|
+
/** Rule that produced the findings. */
|
|
25
|
+
readonly rule: ConstraintRule;
|
|
26
|
+
/** Rule execution context. */
|
|
27
|
+
readonly context: RuleContext;
|
|
28
|
+
/** Findings emitted for this rule. */
|
|
29
|
+
readonly findings: ConstraintFinding[];
|
|
30
|
+
/** Effective fix metadata after authority enforcement. */
|
|
31
|
+
readonly fix: EffectiveFix;
|
|
32
|
+
}
|
|
33
|
+
/** Provider that turns findings into byte-range fixes. */
|
|
34
|
+
export interface RuleFixerProvider {
|
|
35
|
+
/** Produce fixes for mechanically fixable findings. */
|
|
36
|
+
createFixes(input: RuleFixerInput): Fix[] | Promise<Fix[]>;
|
|
37
|
+
}
|
|
38
|
+
/** Result of applying or previewing a batch of fixes. */
|
|
39
|
+
export interface FixApplicationResult {
|
|
40
|
+
/** Files changed in memory or on disk. */
|
|
41
|
+
readonly changedFiles: string[];
|
|
42
|
+
/** Fixes successfully applied. */
|
|
43
|
+
readonly applied: Fix[];
|
|
44
|
+
/** Fixes skipped because they overlapped an already-applied edit. */
|
|
45
|
+
readonly deferred: Fix[];
|
|
46
|
+
/** Unified diff when dry-run mode is requested. */
|
|
47
|
+
readonly diff: string;
|
|
48
|
+
}
|
|
49
|
+
/** Dependencies required for the built-in fixer registry. */
|
|
50
|
+
export interface BuiltInFixersDeps {
|
|
51
|
+
/** Resolver registry, required for TestStubFixer. */
|
|
52
|
+
resolvers: CapabilityRegistry<TestPathResolver>;
|
|
53
|
+
}
|
|
54
|
+
/** Registry of built-in rule fixer providers keyed by evaluator type. */
|
|
55
|
+
export declare function builtInFixers(host?: BuiltInFixersDeps, exec?: ProcessExecutor): Map<string, RuleFixerProvider>;
|
|
56
|
+
/** Resolve a workdir-relative or absolute path to an absolute path. */
|
|
57
|
+
export declare function resolveWorkdirPath(workdir: string, filePath: string): string;
|
|
58
|
+
/** Apply byte-range fixes to files, optionally returning a dry-run diff only. */
|
|
59
|
+
export declare function applyFixes(workdir: string, fixes: readonly Fix[], dryRun?: boolean, fs?: NodeFileSystem): Promise<FixApplicationResult>;
|
|
60
|
+
/** Create a coarse unified diff string for dry-run CLI output. */
|
|
61
|
+
export declare function createUnifiedDiff(filePath: string, before: string, after: string): string;
|
|
62
|
+
/**
|
|
63
|
+
* Fixer provider for `regex` / `rg` evaluators.
|
|
64
|
+
*
|
|
65
|
+
* For each finding with a known line number, reads the file and emits a Fix that
|
|
66
|
+
* replaces every regex match on that line using `rule.fix.replacement` as the
|
|
67
|
+
* replacement string (supports `$1` / `$&` back-references).
|
|
68
|
+
*/
|
|
69
|
+
export declare class RegexFixerProvider implements RuleFixerProvider {
|
|
70
|
+
private readonly fs;
|
|
71
|
+
/** Produce fixes for regex-evaluator findings. */
|
|
72
|
+
createFixes({ rule, context, findings, fix }: RuleFixerInput): Promise<Fix[]>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Fixer provider for `path` / `file-exist` evaluators.
|
|
76
|
+
*
|
|
77
|
+
* In `auto` mode, when a rule requires a file to be absent (`must: absent`),
|
|
78
|
+
* emits a Fix that replaces the entire file content with an empty string.
|
|
79
|
+
* `applyFixes` interprets a zero-length result spanning the full file as a deletion.
|
|
80
|
+
*/
|
|
81
|
+
export declare class PathFixerProvider implements RuleFixerProvider {
|
|
82
|
+
private readonly fs;
|
|
83
|
+
/** Produce deletion fixes for path-evaluator findings. */
|
|
84
|
+
createFixes({ rule, context, findings, fix }: RuleFixerInput): Promise<Fix[]>;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=fixers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixers.d.ts","sourceRoot":"","sources":["../../src/fixers/fixers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG7F,6EAA6E;AAC7E,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAIjD,CAAC;AAEF,8DAA8D;AAC9D,MAAM,WAAW,YAAY;IACzB,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,+DAA+D;IAC/D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,6CAA6C;IAC7C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC7C;AAED,6CAA6C;AAC7C,MAAM,WAAW,cAAc;IAC3B,uCAAuC;IACvC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,8BAA8B;IAC9B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,sCAAsC;IACtC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IACvC,0DAA0D;IAC1D,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC;CAC9B;AAED,0DAA0D;AAC1D,MAAM,WAAW,iBAAiB;IAC9B,uDAAuD;IACvD,WAAW,CAAC,KAAK,EAAE,cAAc,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;CAC9D;AAED,yDAAyD;AACzD,MAAM,WAAW,oBAAoB;IACjC,0CAA0C;IAC1C,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC;IAChC,kCAAkC;IAClC,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC;IACxB,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC;IACzB,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACzB;AAED,6DAA6D;AAC7D,MAAM,WAAW,iBAAiB;IAC9B,qDAAqD;IACrD,SAAS,EAAE,kBAAkB,CAAC,gBAAgB,CAAC,CAAC;CACnD;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,IAAI,CAAC,EAAE,iBAAiB,EAAE,IAAI,CAAC,EAAE,eAAe,GAAG,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAa9G;AAED,uEAAuE;AACvE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE5E;AAED,iFAAiF;AACjF,wBAAsB,UAAU,CAC5B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,SAAS,GAAG,EAAE,EACrB,MAAM,UAAQ,EACd,EAAE,GAAE,cAAqC,GAC1C,OAAO,CAAC,oBAAoB,CAAC,CA0D/B;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAOzF;AA8BD;;;;;;GAMG;AACH,qBAAa,kBAAmB,YAAW,iBAAiB;IACxD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAwB;IAE3C,kDAAkD;IAC5C,WAAW,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;CAkCtF;AAED;;;;;;GAMG;AACH,qBAAa,iBAAkB,YAAW,iBAAiB;IACvD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAwB;IAE3C,0DAA0D;IACpD,WAAW,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;CAoBtF"}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core fixer pipeline: provider contracts, built-in fixer implementations,
|
|
3
|
+
* and the byte-range fix application function.
|
|
4
|
+
*
|
|
5
|
+
* @module rule-engine/fixers
|
|
6
|
+
*/
|
|
7
|
+
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
8
|
+
import { NodeFileSystem } from '@gobing-ai/ts-runtime';
|
|
9
|
+
import { TestStubFixer } from './test-stub-fixer.js';
|
|
10
|
+
/** Numeric ordering for fix authority; higher means more write authority. */
|
|
11
|
+
export const FIX_MODE_RANK = {
|
|
12
|
+
none: 0,
|
|
13
|
+
suggest: 1,
|
|
14
|
+
auto: 2,
|
|
15
|
+
};
|
|
16
|
+
/** Registry of built-in rule fixer providers keyed by evaluator type. */
|
|
17
|
+
export function builtInFixers(host, exec) {
|
|
18
|
+
const regexFixer = new RegexFixerProvider();
|
|
19
|
+
const pathFixer = new PathFixerProvider();
|
|
20
|
+
const entries = [
|
|
21
|
+
['regex', regexFixer],
|
|
22
|
+
['rg', regexFixer],
|
|
23
|
+
['path', pathFixer],
|
|
24
|
+
['file-exist', pathFixer],
|
|
25
|
+
];
|
|
26
|
+
if (host && exec) {
|
|
27
|
+
entries.push(['test-location', new TestStubFixer({ resolvers: host.resolvers, processExecutor: exec })]);
|
|
28
|
+
}
|
|
29
|
+
return new Map(entries);
|
|
30
|
+
}
|
|
31
|
+
/** Resolve a workdir-relative or absolute path to an absolute path. */
|
|
32
|
+
export function resolveWorkdirPath(workdir, filePath) {
|
|
33
|
+
return isAbsolute(filePath) ? filePath : join(workdir, filePath);
|
|
34
|
+
}
|
|
35
|
+
/** Apply byte-range fixes to files, optionally returning a dry-run diff only. */
|
|
36
|
+
export async function applyFixes(workdir, fixes, dryRun = false, fs = new NodeFileSystem()) {
|
|
37
|
+
const byFile = new Map();
|
|
38
|
+
for (const fix of fixes) {
|
|
39
|
+
const list = byFile.get(fix.filePath) ?? [];
|
|
40
|
+
list.push(fix);
|
|
41
|
+
byFile.set(fix.filePath, list);
|
|
42
|
+
}
|
|
43
|
+
const applied = [];
|
|
44
|
+
const deferred = [];
|
|
45
|
+
const changedFiles = [];
|
|
46
|
+
const diffs = [];
|
|
47
|
+
for (const [filePath, fileFixes] of byFile) {
|
|
48
|
+
const absPath = resolveWorkdirPath(workdir, filePath);
|
|
49
|
+
if (!isInsideWorkdir(workdir, absPath)) {
|
|
50
|
+
deferred.push(...fileFixes);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const fileExists = await fs.exists(absPath);
|
|
54
|
+
const original = fileExists ? await fs.readFile(absPath) : '';
|
|
55
|
+
const selected = selectNonOverlappingFixes(fileFixes);
|
|
56
|
+
deferred.push(...selected.deferred);
|
|
57
|
+
let next = original;
|
|
58
|
+
for (const fix of [...selected.applied].sort((a, b) => b.start - a.start)) {
|
|
59
|
+
if (!fileExists && (fix.start !== 0 || fix.end !== 0)) {
|
|
60
|
+
deferred.push(fix);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (!isValidRange(fix.start, fix.end, original.length)) {
|
|
64
|
+
deferred.push(fix);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
next = `${next.slice(0, fix.start)}${fix.replacement}${next.slice(fix.end)}`;
|
|
68
|
+
applied.push(fix);
|
|
69
|
+
}
|
|
70
|
+
if (next === original) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
changedFiles.push(filePath);
|
|
74
|
+
diffs.push(createUnifiedDiff(filePath, original, next));
|
|
75
|
+
if (!dryRun) {
|
|
76
|
+
const isFullDeletion = next.length === 0 && selected.applied.some((fix) => fix.start === 0 && fix.end === original.length);
|
|
77
|
+
if (isFullDeletion) {
|
|
78
|
+
await fs.unlink(absPath);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
await fs.writeFile(absPath, next);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { changedFiles, applied, deferred, diff: diffs.join('\n') };
|
|
86
|
+
}
|
|
87
|
+
/** Create a coarse unified diff string for dry-run CLI output. */
|
|
88
|
+
export function createUnifiedDiff(filePath, before, after) {
|
|
89
|
+
const beforeLines = before.split('\n');
|
|
90
|
+
const afterLines = after.split('\n');
|
|
91
|
+
const lines = [`--- ${filePath}`, `+++ ${filePath}`, `@@ -1,${beforeLines.length} +1,${afterLines.length} @@`];
|
|
92
|
+
lines.push(...beforeLines.map((line) => `-${line}`));
|
|
93
|
+
lines.push(...afterLines.map((line) => `+${line}`));
|
|
94
|
+
return lines.join('\n');
|
|
95
|
+
}
|
|
96
|
+
/** Select the maximal non-overlapping set of fixes, ordered by ascending start offset. */
|
|
97
|
+
function selectNonOverlappingFixes(fixes) {
|
|
98
|
+
const applied = [];
|
|
99
|
+
const deferred = [];
|
|
100
|
+
const ordered = [...fixes].sort((a, b) => a.start - b.start || a.end - b.end);
|
|
101
|
+
let lastEnd = -1;
|
|
102
|
+
for (const fix of ordered) {
|
|
103
|
+
if (fix.start < lastEnd) {
|
|
104
|
+
deferred.push(fix);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
applied.push(fix);
|
|
108
|
+
lastEnd = fix.end;
|
|
109
|
+
}
|
|
110
|
+
return { applied, deferred };
|
|
111
|
+
}
|
|
112
|
+
/** Return true when absPath is at or below workdir. */
|
|
113
|
+
function isInsideWorkdir(workdir, absPath) {
|
|
114
|
+
const rel = relative(resolve(workdir), resolve(absPath));
|
|
115
|
+
return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
|
|
116
|
+
}
|
|
117
|
+
/** Return true when [start, end] is a valid byte range for a string of contentLength bytes. */
|
|
118
|
+
function isValidRange(start, end, contentLength) {
|
|
119
|
+
return start >= 0 && end >= start && end <= contentLength;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Fixer provider for `regex` / `rg` evaluators.
|
|
123
|
+
*
|
|
124
|
+
* For each finding with a known line number, reads the file and emits a Fix that
|
|
125
|
+
* replaces every regex match on that line using `rule.fix.replacement` as the
|
|
126
|
+
* replacement string (supports `$1` / `$&` back-references).
|
|
127
|
+
*/
|
|
128
|
+
export class RegexFixerProvider {
|
|
129
|
+
fs = new NodeFileSystem();
|
|
130
|
+
/** Produce fixes for regex-evaluator findings. */
|
|
131
|
+
async createFixes({ rule, context, findings, fix }) {
|
|
132
|
+
if (fix.mode === 'none' || fix.replacement === undefined)
|
|
133
|
+
return [];
|
|
134
|
+
// After the guard above, mode is guaranteed to not be 'none'.
|
|
135
|
+
const fixMode = fix.mode;
|
|
136
|
+
const replacement = fix.replacement;
|
|
137
|
+
const pattern = rule.evaluator.config?.pattern;
|
|
138
|
+
if (typeof pattern !== 'string' || pattern.length === 0)
|
|
139
|
+
return [];
|
|
140
|
+
const flags = flagsFromRule(rule);
|
|
141
|
+
const regex = new RegExp(pattern, flags.includes('g') ? flags : `${flags}g`);
|
|
142
|
+
const fixes = [];
|
|
143
|
+
for (const finding of findings) {
|
|
144
|
+
if (!finding.filePath)
|
|
145
|
+
continue;
|
|
146
|
+
const absPath = resolveWorkdirPath(context.workdir, finding.filePath);
|
|
147
|
+
if (!isInsideWorkdir(context.workdir, absPath) || finding.line == null)
|
|
148
|
+
continue;
|
|
149
|
+
if (!(await this.fs.exists(absPath)))
|
|
150
|
+
continue;
|
|
151
|
+
const source = await this.fs.readFile(absPath);
|
|
152
|
+
const line = getLineRange(source, finding.line);
|
|
153
|
+
if (!line)
|
|
154
|
+
continue;
|
|
155
|
+
for (const match of source.slice(line.start, line.end).matchAll(regex)) {
|
|
156
|
+
if (match.index === undefined)
|
|
157
|
+
continue;
|
|
158
|
+
const matched = match[0];
|
|
159
|
+
fixes.push({
|
|
160
|
+
ruleId: rule.id,
|
|
161
|
+
filePath: finding.filePath,
|
|
162
|
+
start: line.start + match.index,
|
|
163
|
+
end: line.start + match.index + matched.length,
|
|
164
|
+
replacement: matched.replace(new RegExp(pattern, flags), replacement),
|
|
165
|
+
mode: fixMode,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return fixes;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Fixer provider for `path` / `file-exist` evaluators.
|
|
174
|
+
*
|
|
175
|
+
* In `auto` mode, when a rule requires a file to be absent (`must: absent`),
|
|
176
|
+
* emits a Fix that replaces the entire file content with an empty string.
|
|
177
|
+
* `applyFixes` interprets a zero-length result spanning the full file as a deletion.
|
|
178
|
+
*/
|
|
179
|
+
export class PathFixerProvider {
|
|
180
|
+
fs = new NodeFileSystem();
|
|
181
|
+
/** Produce deletion fixes for path-evaluator findings. */
|
|
182
|
+
async createFixes({ rule, context, findings, fix }) {
|
|
183
|
+
if (fix.mode !== 'auto' || rule.evaluator.config?.must !== 'absent')
|
|
184
|
+
return [];
|
|
185
|
+
const fixes = [];
|
|
186
|
+
for (const finding of findings) {
|
|
187
|
+
if (!finding.filePath)
|
|
188
|
+
continue;
|
|
189
|
+
const absPath = resolveWorkdirPath(context.workdir, finding.filePath);
|
|
190
|
+
if (!isInsideWorkdir(context.workdir, absPath))
|
|
191
|
+
continue;
|
|
192
|
+
if (!(await this.fs.exists(absPath)))
|
|
193
|
+
continue;
|
|
194
|
+
const content = await this.fs.readFile(absPath);
|
|
195
|
+
fixes.push({
|
|
196
|
+
ruleId: rule.id,
|
|
197
|
+
filePath: finding.filePath,
|
|
198
|
+
start: 0,
|
|
199
|
+
end: content.length,
|
|
200
|
+
replacement: '',
|
|
201
|
+
mode: 'auto',
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
return fixes;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/** Extract regex flags from a rule's evaluator config. */
|
|
208
|
+
function flagsFromRule(rule) {
|
|
209
|
+
const raw = rule.evaluator.config?.flags;
|
|
210
|
+
if (typeof raw !== 'string')
|
|
211
|
+
return '';
|
|
212
|
+
if (raw.startsWith('(?') && raw.endsWith(')')) {
|
|
213
|
+
return raw.slice(2, -1).replace(/[^imsuy]/g, '');
|
|
214
|
+
}
|
|
215
|
+
return raw.replace(/[^imsuy]/g, '');
|
|
216
|
+
}
|
|
217
|
+
/** Return the byte range [start, end) of a one-based line number within source. */
|
|
218
|
+
function getLineRange(source, oneBasedLine) {
|
|
219
|
+
if (oneBasedLine < 1)
|
|
220
|
+
return null;
|
|
221
|
+
let start = 0;
|
|
222
|
+
for (let line = 1; line < oneBasedLine; line += 1) {
|
|
223
|
+
const next = source.indexOf('\n', start);
|
|
224
|
+
if (next === -1)
|
|
225
|
+
return null;
|
|
226
|
+
start = next + 1;
|
|
227
|
+
}
|
|
228
|
+
const newline = source.indexOf('\n', start);
|
|
229
|
+
return { start, end: newline === -1 ? source.length : newline };
|
|
230
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixer that generates test file skeletons for source files missing tests.
|
|
3
|
+
*
|
|
4
|
+
* Resolver-aware: selects the correct {@link TestPathResolver} from
|
|
5
|
+
* `rule.evaluator.config.resolver`. Passes empty exports to the resolver's
|
|
6
|
+
* `generateSkeleton` — AST / sg discovery is intentionally omitted here to
|
|
7
|
+
* keep the shared package dependency-free; callers that need export-aware
|
|
8
|
+
* skeletons should register a custom resolver that performs its own discovery.
|
|
9
|
+
* Never overwrites an existing test file.
|
|
10
|
+
*
|
|
11
|
+
* @module rule-engine/fixers/test-stub-fixer
|
|
12
|
+
*/
|
|
13
|
+
import { type ProcessExecutor } from '@gobing-ai/ts-runtime';
|
|
14
|
+
import type { CapabilityRegistry } from '../host/capability-registry';
|
|
15
|
+
import type { TestPathResolver } from '../resolvers/test-path-resolver';
|
|
16
|
+
import type { Fix } from '../types';
|
|
17
|
+
import type { RuleFixerInput, RuleFixerProvider } from './fixers';
|
|
18
|
+
/** File-system seam for dependency injection in tests. */
|
|
19
|
+
export interface TestStubFileSystem {
|
|
20
|
+
/** Resolve to true if the path exists on disk. */
|
|
21
|
+
exists(path: string): Promise<boolean>;
|
|
22
|
+
}
|
|
23
|
+
/** Real file-system implementation backed by the runtime FileSystem seam. */
|
|
24
|
+
export declare const realFileSystem: TestStubFileSystem;
|
|
25
|
+
/** Dependencies injected into {@link TestStubFixer}. */
|
|
26
|
+
export interface TestStubFixerDeps {
|
|
27
|
+
/** Resolver registry from the engine host. */
|
|
28
|
+
readonly resolvers: CapabilityRegistry<TestPathResolver>;
|
|
29
|
+
/** Process executor (reserved for future export discovery). */
|
|
30
|
+
readonly processExecutor: ProcessExecutor;
|
|
31
|
+
/** Optional file-system override for testing. */
|
|
32
|
+
readonly fileSystem?: TestStubFileSystem;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Fixer provider for the `test-location` evaluator.
|
|
36
|
+
*
|
|
37
|
+
* Emits a create-file Fix (start=0, end=0) for each finding whose source file
|
|
38
|
+
* has no corresponding test file yet. The test path and skeleton content are
|
|
39
|
+
* produced by the resolver named in `rule.evaluator.config.resolver`
|
|
40
|
+
* (default: `typescript`).
|
|
41
|
+
*/
|
|
42
|
+
export declare class TestStubFixer implements RuleFixerProvider {
|
|
43
|
+
private readonly resolvers;
|
|
44
|
+
private readonly fs;
|
|
45
|
+
constructor(deps: TestStubFixerDeps);
|
|
46
|
+
/** Produce create-file fixes for missing test stubs. */
|
|
47
|
+
createFixes(input: RuleFixerInput): Promise<Fix[]>;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=test-stub-fixer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-stub-fixer.d.ts","sourceRoot":"","sources":["../../src/fixers/test-stub-fixer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAElE,0DAA0D;AAC1D,MAAM,WAAW,kBAAkB;IAC/B,kDAAkD;IAClD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1C;AAED,6EAA6E;AAC7E,eAAO,MAAM,cAAc,EAAE,kBAAyC,CAAC;AAEvE,wDAAwD;AACxD,MAAM,WAAW,iBAAiB;IAC9B,8CAA8C;IAC9C,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC,gBAAgB,CAAC,CAAC;IACzD,+DAA+D;IAC/D,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,iDAAiD;IACjD,QAAQ,CAAC,UAAU,CAAC,EAAE,kBAAkB,CAAC;CAC5C;AAUD;;;;;;;GAOG;AACH,qBAAa,aAAc,YAAW,iBAAiB;IACnD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuC;IACjE,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAqB;gBAE5B,IAAI,EAAE,iBAAiB;IAOnC,wDAAwD;IAClD,WAAW,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;CAkD3D"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixer that generates test file skeletons for source files missing tests.
|
|
3
|
+
*
|
|
4
|
+
* Resolver-aware: selects the correct {@link TestPathResolver} from
|
|
5
|
+
* `rule.evaluator.config.resolver`. Passes empty exports to the resolver's
|
|
6
|
+
* `generateSkeleton` — AST / sg discovery is intentionally omitted here to
|
|
7
|
+
* keep the shared package dependency-free; callers that need export-aware
|
|
8
|
+
* skeletons should register a custom resolver that performs its own discovery.
|
|
9
|
+
* Never overwrites an existing test file.
|
|
10
|
+
*
|
|
11
|
+
* @module rule-engine/fixers/test-stub-fixer
|
|
12
|
+
*/
|
|
13
|
+
import { isAbsolute, join } from 'node:path';
|
|
14
|
+
import { NodeFileSystem } from '@gobing-ai/ts-runtime';
|
|
15
|
+
/** Real file-system implementation backed by the runtime FileSystem seam. */
|
|
16
|
+
export const realFileSystem = new NodeFileSystem();
|
|
17
|
+
/** Normalize a finding path to a repository-relative POSIX path, or return null when invalid. */
|
|
18
|
+
function normalizeFindingPath(filePath) {
|
|
19
|
+
if (!filePath || isAbsolute(filePath))
|
|
20
|
+
return null;
|
|
21
|
+
const normalized = filePath.replaceAll('\\', '/').replace(/^\.\//, '');
|
|
22
|
+
if (normalized === '..' || normalized.startsWith('../') || normalized.includes('/../'))
|
|
23
|
+
return null;
|
|
24
|
+
return normalized;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Fixer provider for the `test-location` evaluator.
|
|
28
|
+
*
|
|
29
|
+
* Emits a create-file Fix (start=0, end=0) for each finding whose source file
|
|
30
|
+
* has no corresponding test file yet. The test path and skeleton content are
|
|
31
|
+
* produced by the resolver named in `rule.evaluator.config.resolver`
|
|
32
|
+
* (default: `typescript`).
|
|
33
|
+
*/
|
|
34
|
+
export class TestStubFixer {
|
|
35
|
+
resolvers;
|
|
36
|
+
fs;
|
|
37
|
+
constructor(deps) {
|
|
38
|
+
this.resolvers = deps.resolvers;
|
|
39
|
+
// processExecutor is accepted for interface parity but export discovery
|
|
40
|
+
// is not performed in this shared-package implementation — see module doc.
|
|
41
|
+
this.fs = deps.fileSystem ?? realFileSystem;
|
|
42
|
+
}
|
|
43
|
+
/** Produce create-file fixes for missing test stubs. */
|
|
44
|
+
async createFixes(input) {
|
|
45
|
+
const { rule, context, findings, fix } = input;
|
|
46
|
+
if (fix.mode === 'none')
|
|
47
|
+
return [];
|
|
48
|
+
const resolverName = rule.evaluator.config?.resolver ?? 'typescript';
|
|
49
|
+
let resolver;
|
|
50
|
+
try {
|
|
51
|
+
resolver = this.resolvers.get(resolverName);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Resolver not registered — return no fixes rather than silently falling back.
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
const result = [];
|
|
58
|
+
for (const finding of findings) {
|
|
59
|
+
if (!finding.filePath)
|
|
60
|
+
continue;
|
|
61
|
+
const srcRelPath = normalizeFindingPath(finding.filePath);
|
|
62
|
+
if (!srcRelPath)
|
|
63
|
+
continue;
|
|
64
|
+
let testRelPath;
|
|
65
|
+
try {
|
|
66
|
+
testRelPath = resolver.resolveTestPath(srcRelPath);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const absTestPath = join(context.workdir, testRelPath);
|
|
72
|
+
if (await this.fs.exists(absTestPath))
|
|
73
|
+
continue;
|
|
74
|
+
// Export discovery is intentionally omitted — pass empty exports array.
|
|
75
|
+
// Resolvers that need per-export stubs should perform their own discovery.
|
|
76
|
+
const skeleton = resolver.generateSkeleton?.(srcRelPath, []) ?? '';
|
|
77
|
+
if (!skeleton)
|
|
78
|
+
continue;
|
|
79
|
+
result.push({
|
|
80
|
+
ruleId: rule.id,
|
|
81
|
+
filePath: testRelPath,
|
|
82
|
+
start: 0,
|
|
83
|
+
end: 0,
|
|
84
|
+
replacement: skeleton,
|
|
85
|
+
// fix.mode !== 'none' is guaranteed by the guard at the top of this method.
|
|
86
|
+
mode: fix.mode,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../../src/host/builtins.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../../src/host/builtins.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAqB7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,4DAA4D;AAC5D,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,eAAe,GAAG,IAAI,CAuBvF"}
|
package/dist/host/builtins.js
CHANGED
|
@@ -2,13 +2,17 @@ import { AgentDetectionEvaluator } from '../evaluators/agent-detection-evaluator
|
|
|
2
2
|
import { CoverageGateEvaluator } from '../evaluators/coverage-gate-evaluator.js';
|
|
3
3
|
import { ExitCodeEvaluator } from '../evaluators/exit-code-evaluator.js';
|
|
4
4
|
import { ForbiddenImportEvaluator } from '../evaluators/forbidden-import-evaluator.js';
|
|
5
|
+
import { ImportBoundaryEvaluator } from '../evaluators/import-boundary-evaluator.js';
|
|
5
6
|
import { PathEvaluator } from '../evaluators/path-evaluator.js';
|
|
6
7
|
import { RegexEvaluator } from '../evaluators/regex-evaluator.js';
|
|
8
|
+
import { SchemaArtifactEvaluator } from '../evaluators/schema-artifact-evaluator.js';
|
|
7
9
|
import { SecretsScannerEvaluator } from '../evaluators/secrets-scanner-evaluator.js';
|
|
10
|
+
import { SgEvaluator } from '../evaluators/sg-evaluator.js';
|
|
8
11
|
import { TestLocationEvaluator } from '../evaluators/test-location-evaluator.js';
|
|
9
12
|
import { TsdocExportEvaluator } from '../evaluators/tsdoc-export-evaluator.js';
|
|
10
13
|
import { JsonFormatter } from '../formatters/json.js';
|
|
11
14
|
import { TextFormatter } from '../formatters/text.js';
|
|
15
|
+
import { GoTestPathResolver, PythonTestPathResolver, RustTestPathResolver, TypeScriptTestPathResolver, } from '../resolvers/test-path-resolver.js';
|
|
12
16
|
/** Register bundled evaluators and formatters on a host. */
|
|
13
17
|
export function registerBuiltins(host, executor) {
|
|
14
18
|
const regex = new RegexEvaluator();
|
|
@@ -23,7 +27,14 @@ export function registerBuiltins(host, executor) {
|
|
|
23
27
|
host.evaluators.register('agent-detection', new AgentDetectionEvaluator(), 'builtin');
|
|
24
28
|
host.evaluators.register('coverage-gate', new CoverageGateEvaluator(), 'builtin');
|
|
25
29
|
host.evaluators.register('tsdoc-export', new TsdocExportEvaluator(), 'builtin');
|
|
26
|
-
host.
|
|
30
|
+
host.resolvers.register('typescript', new TypeScriptTestPathResolver(), 'builtin');
|
|
31
|
+
host.resolvers.register('python', new PythonTestPathResolver(), 'builtin');
|
|
32
|
+
host.resolvers.register('go', new GoTestPathResolver(), 'builtin');
|
|
33
|
+
host.resolvers.register('rust', new RustTestPathResolver(), 'builtin');
|
|
34
|
+
host.evaluators.register('test-location', new TestLocationEvaluator(host.resolvers), 'builtin');
|
|
35
|
+
host.evaluators.register('sg', new SgEvaluator(executor), 'builtin');
|
|
36
|
+
host.evaluators.register('schema-artifact', new SchemaArtifactEvaluator(), 'builtin');
|
|
37
|
+
host.evaluators.register('import-boundary', new ImportBoundaryEvaluator(), 'builtin');
|
|
27
38
|
host.formatters.register('text', new TextFormatter(), 'builtin');
|
|
28
39
|
host.formatters.register('json', new JsonFormatter(), 'builtin');
|
|
29
40
|
}
|