@gobing-ai/ts-rule-engine 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/config/loader.d.ts +13 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +98 -0
- package/dist/engine.d.ts +21 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +36 -0
- package/dist/evaluators/agent-detection-evaluator.d.ts +10 -0
- package/dist/evaluators/agent-detection-evaluator.d.ts.map +1 -0
- package/dist/evaluators/agent-detection-evaluator.js +33 -0
- package/dist/evaluators/exit-code-evaluator.d.ts +10 -0
- package/dist/evaluators/exit-code-evaluator.d.ts.map +1 -0
- package/dist/evaluators/exit-code-evaluator.js +38 -0
- package/dist/evaluators/file-utils.d.ts +23 -0
- package/dist/evaluators/file-utils.d.ts.map +1 -0
- package/dist/evaluators/file-utils.js +35 -0
- package/dist/evaluators/forbidden-import-evaluator.d.ts +8 -0
- package/dist/evaluators/forbidden-import-evaluator.d.ts.map +1 -0
- package/dist/evaluators/forbidden-import-evaluator.js +42 -0
- package/dist/evaluators/path-evaluator.d.ts +9 -0
- package/dist/evaluators/path-evaluator.d.ts.map +1 -0
- package/dist/evaluators/path-evaluator.js +39 -0
- package/dist/evaluators/regex-evaluator.d.ts +8 -0
- package/dist/evaluators/regex-evaluator.d.ts.map +1 -0
- package/dist/evaluators/regex-evaluator.js +36 -0
- package/dist/evaluators/secrets-scanner-evaluator.d.ts +8 -0
- package/dist/evaluators/secrets-scanner-evaluator.d.ts.map +1 -0
- package/dist/evaluators/secrets-scanner-evaluator.js +24 -0
- package/dist/formatters/json.d.ts +8 -0
- package/dist/formatters/json.d.ts.map +1 -0
- package/dist/formatters/json.js +8 -0
- package/dist/formatters/text.d.ts +8 -0
- package/dist/formatters/text.d.ts.map +1 -0
- package/dist/formatters/text.js +17 -0
- package/dist/host/builtins.d.ts +5 -0
- package/dist/host/builtins.d.ts.map +1 -0
- package/dist/host/builtins.js +23 -0
- package/dist/host/capability-registry.d.ts +24 -0
- package/dist/host/capability-registry.d.ts.map +1 -0
- package/dist/host/capability-registry.js +28 -0
- package/dist/host/rule-engine-host.d.ts +11 -0
- package/dist/host/rule-engine-host.d.ts.map +1 -0
- package/dist/host/rule-engine-host.js +12 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/types.d.ts +218 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +49 -0
- package/package.json +61 -0
- package/src/config/loader.ts +122 -0
- package/src/engine.ts +50 -0
- package/src/evaluators/agent-detection-evaluator.ts +37 -0
- package/src/evaluators/exit-code-evaluator.ts +42 -0
- package/src/evaluators/file-utils.ts +55 -0
- package/src/evaluators/forbidden-import-evaluator.ts +50 -0
- package/src/evaluators/path-evaluator.ts +48 -0
- package/src/evaluators/regex-evaluator.ts +49 -0
- package/src/evaluators/secrets-scanner-evaluator.ts +34 -0
- package/src/formatters/json.ts +11 -0
- package/src/formatters/text.ts +20 -0
- package/src/host/builtins.ts +26 -0
- package/src/host/capability-registry.ts +41 -0
- package/src/host/rule-engine-host.ts +15 -0
- package/src/index.ts +7 -0
- package/src/types.ts +197 -0
package/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ConstraintRule } from '../types';
|
|
2
|
+
/** Options for loading rule presets. */
|
|
3
|
+
export interface RuleLoaderOptions {
|
|
4
|
+
/** Project working directory. */
|
|
5
|
+
workdir: string;
|
|
6
|
+
/** Rule root directory. Defaults to ".spur/rules". */
|
|
7
|
+
rulesRoot?: string;
|
|
8
|
+
}
|
|
9
|
+
/** Load and normalize a preset by name. */
|
|
10
|
+
export declare function loadPresetRules(name: string, options: RuleLoaderOptions): Promise<ConstraintRule[]>;
|
|
11
|
+
/** Load a direct rule file from disk. */
|
|
12
|
+
export declare function loadRuleFile(filePath: string): Promise<ConstraintRule[]>;
|
|
13
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAGA,OAAO,EACH,KAAK,cAAc,EAMtB,MAAM,UAAU,CAAC;AAElB,wCAAwC;AACxC,MAAM,WAAW,iBAAiB;IAC9B,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,2CAA2C;AAC3C,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAoBzG;AAED,yCAAyC;AACzC,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAE9E"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { basename, dirname, extname, join, resolve } from 'node:path';
|
|
2
|
+
import { NodeFileSystem } from '@gobing-ai/ts-runtime';
|
|
3
|
+
import { parse } from 'yaml';
|
|
4
|
+
import { ConstraintRuleFileSchema, ConstraintRuleSchema, PresetDefinitionSchema, } from '../types.js';
|
|
5
|
+
/** Load and normalize a preset by name. */
|
|
6
|
+
export async function loadPresetRules(name, options) {
|
|
7
|
+
const fs = new NodeFileSystem();
|
|
8
|
+
const root = resolve(options.workdir, options.rulesRoot ?? '.spur/rules');
|
|
9
|
+
const presetPath = await findDefinitionPath(root, name);
|
|
10
|
+
if (presetPath === null)
|
|
11
|
+
return [];
|
|
12
|
+
const preset = PresetDefinitionSchema.parse(await readStructuredFile(presetPath));
|
|
13
|
+
const rules = [];
|
|
14
|
+
for (const entry of preset.extends) {
|
|
15
|
+
rules.push(...(await loadPresetEntry(root, entry, new Set([name]))));
|
|
16
|
+
}
|
|
17
|
+
const disabled = new Set(preset.disable ?? []);
|
|
18
|
+
const normalized = rules.filter((rule) => !disabled.has(rule.id));
|
|
19
|
+
for (const rule of normalized) {
|
|
20
|
+
const override = preset.overrides?.[rule.id];
|
|
21
|
+
if (override?.fix !== undefined) {
|
|
22
|
+
rule.fix = { ...(rule.fix ?? { mode: 'none' }), ...override.fix };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
await fs.exists(root);
|
|
26
|
+
return normalized;
|
|
27
|
+
}
|
|
28
|
+
/** Load a direct rule file from disk. */
|
|
29
|
+
export async function loadRuleFile(filePath) {
|
|
30
|
+
return normalizeRuleFile(await readStructuredFile(resolve(filePath)), dirname(resolve(filePath)));
|
|
31
|
+
}
|
|
32
|
+
async function loadPresetEntry(root, entry, seen) {
|
|
33
|
+
const presetPath = await findDefinitionPath(root, entry);
|
|
34
|
+
if (presetPath !== null && !seen.has(entry)) {
|
|
35
|
+
seen.add(entry);
|
|
36
|
+
const preset = PresetDefinitionSchema.safeParse(await readStructuredFile(presetPath));
|
|
37
|
+
if (preset.success) {
|
|
38
|
+
const rules = [];
|
|
39
|
+
for (const child of preset.data.extends)
|
|
40
|
+
rules.push(...(await loadPresetEntry(root, child, seen)));
|
|
41
|
+
return rules.filter((rule) => !(preset.data.disable ?? []).includes(rule.id));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const categoryDir = resolve(root, entry);
|
|
45
|
+
const fs = new NodeFileSystem();
|
|
46
|
+
if (!(await fs.exists(categoryDir)))
|
|
47
|
+
return [];
|
|
48
|
+
const entries = (await fs.readDir(categoryDir)).filter((file) => /\.(ya?ml|json)$/i.test(file)).sort();
|
|
49
|
+
const rules = [];
|
|
50
|
+
for (const file of entries) {
|
|
51
|
+
rules.push(...(await loadRuleFile(join(categoryDir, file))));
|
|
52
|
+
}
|
|
53
|
+
return rules;
|
|
54
|
+
}
|
|
55
|
+
async function findDefinitionPath(root, name) {
|
|
56
|
+
const fs = new NodeFileSystem();
|
|
57
|
+
const candidates = [
|
|
58
|
+
resolve(root, `${name}.yaml`),
|
|
59
|
+
resolve(root, `${name}.yml`),
|
|
60
|
+
resolve(root, `${name}.json`),
|
|
61
|
+
resolve(root, name, 'index.yaml'),
|
|
62
|
+
resolve(root, name, 'index.yml'),
|
|
63
|
+
resolve(root, name, 'index.json'),
|
|
64
|
+
];
|
|
65
|
+
for (const candidate of candidates) {
|
|
66
|
+
if (await fs.exists(candidate))
|
|
67
|
+
return candidate;
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
async function readStructuredFile(path) {
|
|
72
|
+
const content = await new NodeFileSystem().readFile(path);
|
|
73
|
+
return extname(path) === '.json' ? JSON.parse(content) : parse(content);
|
|
74
|
+
}
|
|
75
|
+
function normalizeRuleFile(raw, sourceDir) {
|
|
76
|
+
const maybeFile = ConstraintRuleFileSchema.safeParse(raw);
|
|
77
|
+
if (maybeFile.success)
|
|
78
|
+
return normalizeFileRules(maybeFile.data, sourceDir);
|
|
79
|
+
const maybeRule = ConstraintRuleSchema.safeParse(raw);
|
|
80
|
+
if (maybeRule.success)
|
|
81
|
+
return [normalizeRule(maybeRule.data, {}, sourceDir)];
|
|
82
|
+
throw new Error(`Invalid rule file: ${basename(sourceDir)}`);
|
|
83
|
+
}
|
|
84
|
+
function normalizeFileRules(file, sourceDir) {
|
|
85
|
+
return file.rules.map((rule) => normalizeRule({
|
|
86
|
+
...rule,
|
|
87
|
+
severity: rule.severity ?? file.severity ?? 'error',
|
|
88
|
+
include: rule.include ?? file.include,
|
|
89
|
+
exclude: rule.exclude ?? file.exclude,
|
|
90
|
+
}, {}, sourceDir));
|
|
91
|
+
}
|
|
92
|
+
function normalizeRule(rule, _defaults, _sourceDir) {
|
|
93
|
+
return {
|
|
94
|
+
...rule,
|
|
95
|
+
enabled: rule.enabled ?? true,
|
|
96
|
+
severity: rule.severity ?? 'error',
|
|
97
|
+
};
|
|
98
|
+
}
|
package/dist/engine.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ProcessExecutor } from '@gobing-ai/ts-runtime';
|
|
2
|
+
import { RuleEngineHost } from './host/rule-engine-host';
|
|
3
|
+
import type { ConstraintRule, RuleEngineResult, RuleEvaluator } from './types';
|
|
4
|
+
/** Options for constructing a RuleEngine. */
|
|
5
|
+
export interface RuleEngineOptions {
|
|
6
|
+
/** Optional executor supplied to process-backed evaluators. */
|
|
7
|
+
processExecutor?: ProcessExecutor;
|
|
8
|
+
/** Optional preconfigured host. */
|
|
9
|
+
host?: RuleEngineHost;
|
|
10
|
+
}
|
|
11
|
+
/** Orchestrates enabled constraint rules through a typed evaluator host. */
|
|
12
|
+
export declare class RuleEngine {
|
|
13
|
+
/** Capability host used by this engine. */
|
|
14
|
+
readonly host: RuleEngineHost;
|
|
15
|
+
constructor(options?: RuleEngineOptions);
|
|
16
|
+
/** Register or replace an evaluator. */
|
|
17
|
+
registerEvaluator(type: string, evaluator: RuleEvaluator): void;
|
|
18
|
+
/** Evaluate all enabled rules against a working directory. */
|
|
19
|
+
evaluate(rules: ConstraintRule[], workdir: string): Promise<RuleEngineResult>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,KAAK,EAAqB,cAAc,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGlG,6CAA6C;AAC7C,MAAM,WAAW,iBAAiB;IAC9B,+DAA+D;IAC/D,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,mCAAmC;IACnC,IAAI,CAAC,EAAE,cAAc,CAAC;CACzB;AAED,4EAA4E;AAC5E,qBAAa,UAAU;IACnB,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;gBAElB,OAAO,GAAE,iBAAsB;IAK3C,wCAAwC;IACxC,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,GAAG,IAAI;IAI/D,8DAA8D;IACxD,QAAQ,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAmBtF"}
|
package/dist/engine.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { registerBuiltins } from './host/builtins.js';
|
|
2
|
+
import { RuleEngineHost } from './host/rule-engine-host.js';
|
|
3
|
+
import { createFinding } from './types.js';
|
|
4
|
+
/** Orchestrates enabled constraint rules through a typed evaluator host. */
|
|
5
|
+
export class RuleEngine {
|
|
6
|
+
/** Capability host used by this engine. */
|
|
7
|
+
host;
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
this.host = options.host ?? new RuleEngineHost();
|
|
10
|
+
registerBuiltins(this.host, options.processExecutor);
|
|
11
|
+
}
|
|
12
|
+
/** Register or replace an evaluator. */
|
|
13
|
+
registerEvaluator(type, evaluator) {
|
|
14
|
+
this.host.evaluators.register(type, evaluator, 'extension');
|
|
15
|
+
}
|
|
16
|
+
/** Evaluate all enabled rules against a working directory. */
|
|
17
|
+
async evaluate(rules, workdir) {
|
|
18
|
+
const findings = [];
|
|
19
|
+
const fixes = [];
|
|
20
|
+
for (const rule of rules) {
|
|
21
|
+
if (rule.enabled === false)
|
|
22
|
+
continue;
|
|
23
|
+
try {
|
|
24
|
+
const result = await this.host.evaluators.get(rule.evaluator.type).evaluate(rule, { rule, workdir });
|
|
25
|
+
findings.push(...result.findings);
|
|
26
|
+
fixes.push(...result.fixes);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
findings.push(createFinding(rule, error instanceof Error ? error.message : String(error), null, {
|
|
30
|
+
code: `evaluator:${rule.evaluator.type}`,
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return { findings, fixes };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AgentDetector } from '@gobing-ai/ts-ai-runner';
|
|
2
|
+
import { type ConstraintRule, type RuleContext, type RuleEvaluationResult, type RuleEvaluator } from '../types';
|
|
3
|
+
/** Evaluates local availability of configured coding agents. */
|
|
4
|
+
export declare class AgentDetectionEvaluator implements RuleEvaluator {
|
|
5
|
+
private readonly detector;
|
|
6
|
+
constructor(detector?: AgentDetector);
|
|
7
|
+
/** Probe required agents and emit findings for missing CLIs. */
|
|
8
|
+
evaluate(rule: ConstraintRule, _context: RuleContext): Promise<RuleEvaluationResult>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=agent-detection-evaluator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-detection-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/agent-detection-evaluator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA+B,MAAM,yBAAyB,CAAC;AACrF,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAElB,gEAAgE;AAChE,qBAAa,uBAAwB,YAAW,aAAa;IAC7C,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,gBAAsB;IAE3D,gEAAgE;IAC1D,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAe7F"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { AgentDetector, isAgentName } from '@gobing-ai/ts-ai-runner';
|
|
2
|
+
import { createFinding, } from '../types.js';
|
|
3
|
+
/** Evaluates local availability of configured coding agents. */
|
|
4
|
+
export class AgentDetectionEvaluator {
|
|
5
|
+
detector;
|
|
6
|
+
constructor(detector = new AgentDetector()) {
|
|
7
|
+
this.detector = detector;
|
|
8
|
+
}
|
|
9
|
+
/** Probe required agents and emit findings for missing CLIs. */
|
|
10
|
+
async evaluate(rule, _context) {
|
|
11
|
+
const agents = arrayConfig(rule.evaluator.config ?? {}, 'agents');
|
|
12
|
+
const findings = [];
|
|
13
|
+
for (const agent of agents) {
|
|
14
|
+
if (!isAgentName(agent)) {
|
|
15
|
+
findings.push(createFinding(rule, `Unknown agent: ${agent}`, null, { code: 'agent:unknown' }));
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
const detected = await this.detector.detectOne(agent);
|
|
19
|
+
if (!detected.installed) {
|
|
20
|
+
findings.push(createFinding(rule, `Agent unavailable: ${agent}`, null, { code: 'agent:missing' }));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return { findings, fixes: [] };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function arrayConfig(config, key) {
|
|
27
|
+
const value = config[key];
|
|
28
|
+
if (Array.isArray(value) && value.every((item) => typeof item === 'string'))
|
|
29
|
+
return value;
|
|
30
|
+
if (typeof value === 'string')
|
|
31
|
+
return [value];
|
|
32
|
+
throw new Error(`agent-detection evaluator requires string[] config "${key}"`);
|
|
33
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ProcessExecutor } from '@gobing-ai/ts-runtime';
|
|
2
|
+
import { type ConstraintRule, type RuleContext, type RuleEvaluationResult, type RuleEvaluator } from '../types';
|
|
3
|
+
/** Evaluates a rule by running a subprocess and checking its exit code. */
|
|
4
|
+
export declare class ExitCodeEvaluator implements RuleEvaluator {
|
|
5
|
+
private readonly executor;
|
|
6
|
+
constructor(executor?: ProcessExecutor);
|
|
7
|
+
/** Run configured command and emit a finding on non-zero exit. */
|
|
8
|
+
evaluate(rule: ConstraintRule, context: RuleContext): Promise<RuleEvaluationResult>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=exit-code-evaluator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exit-code-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/exit-code-evaluator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAElB,2EAA2E;AAC3E,qBAAa,iBAAkB,YAAW,aAAa;IACvC,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,GAAE,eAA2C;IAElF,kEAAkE;IAC5D,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAe5F"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { NodeProcessExecutor } from '@gobing-ai/ts-runtime';
|
|
2
|
+
import { createFinding, } from '../types.js';
|
|
3
|
+
/** Evaluates a rule by running a subprocess and checking its exit code. */
|
|
4
|
+
export class ExitCodeEvaluator {
|
|
5
|
+
executor;
|
|
6
|
+
constructor(executor = new NodeProcessExecutor()) {
|
|
7
|
+
this.executor = executor;
|
|
8
|
+
}
|
|
9
|
+
/** Run configured command and emit a finding on non-zero exit. */
|
|
10
|
+
async evaluate(rule, context) {
|
|
11
|
+
const config = rule.evaluator.config ?? {};
|
|
12
|
+
const command = stringConfig(config, 'command');
|
|
13
|
+
const args = arrayConfig(config, 'args', []);
|
|
14
|
+
const result = await this.executor.run({ command, args, cwd: context.workdir, rejectOnError: false });
|
|
15
|
+
if (result.exitCode === 0)
|
|
16
|
+
return { findings: [], fixes: [] };
|
|
17
|
+
return {
|
|
18
|
+
findings: [
|
|
19
|
+
createFinding(rule, `Command failed: ${command} ${args.join(' ')}`.trim(), null, {
|
|
20
|
+
code: 'exit-code:failed',
|
|
21
|
+
}),
|
|
22
|
+
],
|
|
23
|
+
fixes: [],
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function stringConfig(config, key) {
|
|
28
|
+
const value = config[key];
|
|
29
|
+
if (typeof value === 'string')
|
|
30
|
+
return value;
|
|
31
|
+
throw new Error(`exit-code evaluator requires string config "${key}"`);
|
|
32
|
+
}
|
|
33
|
+
function arrayConfig(config, key, fallback) {
|
|
34
|
+
const value = config[key];
|
|
35
|
+
if (Array.isArray(value) && value.every((item) => typeof item === 'string'))
|
|
36
|
+
return value;
|
|
37
|
+
return fallback;
|
|
38
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type FileSystem, NodeFileSystem } from '@gobing-ai/ts-runtime';
|
|
2
|
+
/** Options for source-file discovery. */
|
|
3
|
+
export interface SourceDiscoveryOptions {
|
|
4
|
+
/** Working directory. */
|
|
5
|
+
workdir: string;
|
|
6
|
+
/** Include path fragments or suffixes. */
|
|
7
|
+
include?: string[];
|
|
8
|
+
/** Exclude path fragments. */
|
|
9
|
+
exclude?: string[];
|
|
10
|
+
/** Filesystem adapter. */
|
|
11
|
+
fs?: FileSystem;
|
|
12
|
+
}
|
|
13
|
+
/** Resolve source files for evaluators using conservative path-fragment matching. */
|
|
14
|
+
export declare function discoverFiles(options: SourceDiscoveryOptions): Promise<string[]>;
|
|
15
|
+
/** Read a file from a workdir-relative path. */
|
|
16
|
+
export declare function readWorkdirFile(workdir: string, filePath: string, fs?: NodeFileSystem): Promise<string>;
|
|
17
|
+
/** Ensure a path is workdir-relative for findings. */
|
|
18
|
+
export declare function relativeToWorkdir(workdir: string, path: string): string;
|
|
19
|
+
/** Return parent directory for a workdir-relative path. */
|
|
20
|
+
export declare function relativeParent(path: string): string;
|
|
21
|
+
/** Return true when a path matches any supplied fragment or suffix. */
|
|
22
|
+
export declare function matchesAny(path: string, patterns: string[] | undefined): boolean;
|
|
23
|
+
//# sourceMappingURL=file-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-utils.d.ts","sourceRoot":"","sources":["../../src/evaluators/file-utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,UAAU,EAAE,cAAc,EAAW,MAAM,uBAAuB,CAAC;AAEjF,yCAAyC;AACzC,MAAM,WAAW,sBAAsB;IACnC,yBAAyB;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,0BAA0B;IAC1B,EAAE,CAAC,EAAE,UAAU,CAAC;CACnB;AAID,qFAAqF;AACrF,wBAAsB,aAAa,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAWtF;AAED,gDAAgD;AAChD,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,iBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,CAEnH;AAED,sDAAsD;AACtD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED,2DAA2D;AAC3D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED,uEAAuE;AACvE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAS,GAAG,OAAO,CAMhF"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { dirname, relative, resolve } from 'node:path';
|
|
2
|
+
import { NodeFileSystem, walkDir } from '@gobing-ai/ts-runtime';
|
|
3
|
+
const DEFAULT_EXCLUDES = new Set(['.git', 'node_modules', 'dist', '.coverage', '.astro', '.wrangler']);
|
|
4
|
+
/** Resolve source files for evaluators using conservative path-fragment matching. */
|
|
5
|
+
export async function discoverFiles(options) {
|
|
6
|
+
const fs = options.fs ?? new NodeFileSystem();
|
|
7
|
+
const allFiles = await walkDir(options.workdir, fs);
|
|
8
|
+
return allFiles
|
|
9
|
+
.map((path) => relative(options.workdir, path))
|
|
10
|
+
.filter((path) => !path.split('/').some((segment) => DEFAULT_EXCLUDES.has(segment)))
|
|
11
|
+
.filter((path) => matchesAny(path, options.include) &&
|
|
12
|
+
(options.exclude === undefined || !matchesAny(path, options.exclude)));
|
|
13
|
+
}
|
|
14
|
+
/** Read a file from a workdir-relative path. */
|
|
15
|
+
export async function readWorkdirFile(workdir, filePath, fs = new NodeFileSystem()) {
|
|
16
|
+
return await fs.readFile(resolve(workdir, filePath));
|
|
17
|
+
}
|
|
18
|
+
/** Ensure a path is workdir-relative for findings. */
|
|
19
|
+
export function relativeToWorkdir(workdir, path) {
|
|
20
|
+
return relative(workdir, resolve(path));
|
|
21
|
+
}
|
|
22
|
+
/** Return parent directory for a workdir-relative path. */
|
|
23
|
+
export function relativeParent(path) {
|
|
24
|
+
const parent = dirname(path);
|
|
25
|
+
return parent === '.' ? '' : parent;
|
|
26
|
+
}
|
|
27
|
+
/** Return true when a path matches any supplied fragment or suffix. */
|
|
28
|
+
export function matchesAny(path, patterns) {
|
|
29
|
+
if (patterns === undefined || patterns.length === 0)
|
|
30
|
+
return true;
|
|
31
|
+
return patterns.some((pattern) => {
|
|
32
|
+
const clean = pattern.replaceAll('\\', '/').replaceAll('**/', '').replaceAll('*', '');
|
|
33
|
+
return clean.length === 0 || path.includes(clean) || path.endsWith(clean);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ConstraintRule, type RuleContext, type RuleEvaluationResult, type RuleEvaluator } from '../types';
|
|
2
|
+
/** Detects imports matching forbidden package or path prefixes. */
|
|
3
|
+
export declare class ForbiddenImportEvaluator implements RuleEvaluator {
|
|
4
|
+
constructor();
|
|
5
|
+
/** Evaluate import declarations against configured forbidden prefixes. */
|
|
6
|
+
evaluate(rule: ConstraintRule, context: RuleContext): Promise<RuleEvaluationResult>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=forbidden-import-evaluator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forbidden-import-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/forbidden-import-evaluator.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAGlB,mEAAmE;AACnE,qBAAa,wBAAyB,YAAW,aAAa;;IAG1D,0EAA0E;IACpE,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CA4B5F"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createFinding, } from '../types.js';
|
|
2
|
+
import { discoverFiles, readWorkdirFile } from './file-utils.js';
|
|
3
|
+
/** Detects imports matching forbidden package or path prefixes. */
|
|
4
|
+
export class ForbiddenImportEvaluator {
|
|
5
|
+
constructor() { }
|
|
6
|
+
/** Evaluate import declarations against configured forbidden prefixes. */
|
|
7
|
+
async evaluate(rule, context) {
|
|
8
|
+
const config = rule.evaluator.config ?? {};
|
|
9
|
+
const forbidden = arrayConfig(config, 'patterns');
|
|
10
|
+
const files = await discoverFiles({
|
|
11
|
+
workdir: context.workdir,
|
|
12
|
+
include: rule.include ?? ['.ts', '.tsx', '.js', '.jsx'],
|
|
13
|
+
exclude: rule.exclude,
|
|
14
|
+
});
|
|
15
|
+
const findings = [];
|
|
16
|
+
for (const file of files) {
|
|
17
|
+
const lines = (await readWorkdirFile(context.workdir, file)).split('\n');
|
|
18
|
+
for (const [index, line] of lines.entries()) {
|
|
19
|
+
const imported = /(?:from\s+|import\s*\(|^\s*import\s*)['"](?<specifier>[^'"]+)['"]/.exec(line)?.groups
|
|
20
|
+
?.specifier;
|
|
21
|
+
if (imported === undefined)
|
|
22
|
+
continue;
|
|
23
|
+
const matched = forbidden.find((pattern) => imported.includes(pattern));
|
|
24
|
+
if (matched !== undefined) {
|
|
25
|
+
findings.push(createFinding(rule, `Forbidden import "${imported}" matched "${matched}"`, file, {
|
|
26
|
+
line: index + 1,
|
|
27
|
+
code: 'import:forbidden',
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { findings, fixes: [] };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function arrayConfig(config, key) {
|
|
36
|
+
const value = config[key];
|
|
37
|
+
if (Array.isArray(value) && value.every((item) => typeof item === 'string'))
|
|
38
|
+
return value;
|
|
39
|
+
if (typeof value === 'string')
|
|
40
|
+
return [value];
|
|
41
|
+
throw new Error(`forbidden-import evaluator requires string[] config "${key}"`);
|
|
42
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ConstraintRule, type RuleContext, type RuleEvaluationResult, type RuleEvaluator } from '../types';
|
|
2
|
+
/** Evaluates file or directory existence constraints. */
|
|
3
|
+
export declare class PathEvaluator implements RuleEvaluator {
|
|
4
|
+
private readonly fs;
|
|
5
|
+
constructor();
|
|
6
|
+
/** Evaluate required or forbidden paths. */
|
|
7
|
+
evaluate(rule: ConstraintRule, context: RuleContext): Promise<RuleEvaluationResult>;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=path-evaluator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/path-evaluator.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAElB,yDAAyD;AACzD,qBAAa,aAAc,YAAW,aAAa;IAC/C,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAiB;;IAMpC,4CAA4C;IACtC,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAgB5F"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { NodeFileSystem } from '@gobing-ai/ts-runtime';
|
|
3
|
+
import { createFinding, } from '../types.js';
|
|
4
|
+
/** Evaluates file or directory existence constraints. */
|
|
5
|
+
export class PathEvaluator {
|
|
6
|
+
fs;
|
|
7
|
+
constructor() {
|
|
8
|
+
this.fs = new NodeFileSystem();
|
|
9
|
+
}
|
|
10
|
+
/** Evaluate required or forbidden paths. */
|
|
11
|
+
async evaluate(rule, context) {
|
|
12
|
+
const config = rule.evaluator.config ?? {};
|
|
13
|
+
const paths = arrayConfig(config, 'paths');
|
|
14
|
+
const mode = stringConfig(config, 'mode', 'require');
|
|
15
|
+
const findings = [];
|
|
16
|
+
for (const path of paths) {
|
|
17
|
+
const exists = await this.fs.exists(resolve(context.workdir, path));
|
|
18
|
+
if (mode === 'forbid' && exists) {
|
|
19
|
+
findings.push(createFinding(rule, `Forbidden path exists: ${path}`, path, { code: 'path:forbidden' }));
|
|
20
|
+
}
|
|
21
|
+
if (mode !== 'forbid' && !exists) {
|
|
22
|
+
findings.push(createFinding(rule, `Required path missing: ${path}`, path, { code: 'path:missing' }));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return { findings, fixes: [] };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function arrayConfig(config, key) {
|
|
29
|
+
const value = config[key];
|
|
30
|
+
if (Array.isArray(value) && value.every((item) => typeof item === 'string'))
|
|
31
|
+
return value;
|
|
32
|
+
if (typeof value === 'string')
|
|
33
|
+
return [value];
|
|
34
|
+
throw new Error(`path evaluator requires string[] config "${key}"`);
|
|
35
|
+
}
|
|
36
|
+
function stringConfig(config, key, fallback) {
|
|
37
|
+
const value = config[key];
|
|
38
|
+
return typeof value === 'string' ? value : fallback;
|
|
39
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ConstraintRule, type RuleContext, type RuleEvaluationResult, type RuleEvaluator } from '../types';
|
|
2
|
+
/** Evaluates whether source files match or avoid a regex pattern. */
|
|
3
|
+
export declare class RegexEvaluator implements RuleEvaluator {
|
|
4
|
+
constructor();
|
|
5
|
+
/** Evaluate regex-based presence or absence constraints. */
|
|
6
|
+
evaluate(rule: ConstraintRule, context: RuleContext): Promise<RuleEvaluationResult>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=regex-evaluator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"regex-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/regex-evaluator.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAGlB,qEAAqE;AACrE,qBAAa,cAAe,YAAW,aAAa;;IAGhD,4DAA4D;IACtD,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CA2B5F"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createFinding, } from '../types.js';
|
|
2
|
+
import { discoverFiles, readWorkdirFile } from './file-utils.js';
|
|
3
|
+
/** Evaluates whether source files match or avoid a regex pattern. */
|
|
4
|
+
export class RegexEvaluator {
|
|
5
|
+
constructor() { }
|
|
6
|
+
/** Evaluate regex-based presence or absence constraints. */
|
|
7
|
+
async evaluate(rule, context) {
|
|
8
|
+
const config = rule.evaluator.config ?? {};
|
|
9
|
+
const pattern = stringConfig(config, 'pattern');
|
|
10
|
+
const mode = stringConfig(config, 'mode', 'forbid');
|
|
11
|
+
const flags = stringConfig(config, 'flags', 'm');
|
|
12
|
+
const regex = new RegExp(pattern, flags);
|
|
13
|
+
const files = await discoverFiles({ workdir: context.workdir, include: rule.include, exclude: rule.exclude });
|
|
14
|
+
const findings = [];
|
|
15
|
+
for (const file of files) {
|
|
16
|
+
const content = await readWorkdirFile(context.workdir, file);
|
|
17
|
+
regex.lastIndex = 0;
|
|
18
|
+
const match = regex.exec(content);
|
|
19
|
+
if (mode === 'require' && match === null) {
|
|
20
|
+
findings.push(createFinding(rule, `Required pattern not found: ${pattern}`, file, { code: 'regex:missing' }));
|
|
21
|
+
}
|
|
22
|
+
if (mode !== 'require' && match !== null) {
|
|
23
|
+
findings.push(createFinding(rule, `Forbidden pattern found: ${pattern}`, file, { code: 'regex:found' }));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return { findings, fixes: [] };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function stringConfig(config, key, fallback) {
|
|
30
|
+
const value = config[key];
|
|
31
|
+
if (typeof value === 'string')
|
|
32
|
+
return value;
|
|
33
|
+
if (fallback !== undefined)
|
|
34
|
+
return fallback;
|
|
35
|
+
throw new Error(`regex evaluator requires string config "${key}"`);
|
|
36
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ConstraintRule, type RuleContext, type RuleEvaluationResult, type RuleEvaluator } from '../types';
|
|
2
|
+
/** Scans text files for high-confidence secret-like tokens. */
|
|
3
|
+
export declare class SecretsScannerEvaluator implements RuleEvaluator {
|
|
4
|
+
constructor();
|
|
5
|
+
/** Evaluate source files against bundled secret patterns. */
|
|
6
|
+
evaluate(rule: ConstraintRule, context: RuleContext): Promise<RuleEvaluationResult>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=secrets-scanner-evaluator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets-scanner-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/secrets-scanner-evaluator.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAGlB,+DAA+D;AAC/D,qBAAa,uBAAwB,YAAW,aAAa;;IAGzD,6DAA6D;IACvD,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAmB5F"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createFinding, } from '../types.js';
|
|
2
|
+
import { discoverFiles, readWorkdirFile } from './file-utils.js';
|
|
3
|
+
/** Scans text files for high-confidence secret-like tokens. */
|
|
4
|
+
export class SecretsScannerEvaluator {
|
|
5
|
+
constructor() { }
|
|
6
|
+
/** Evaluate source files against bundled secret patterns. */
|
|
7
|
+
async evaluate(rule, context) {
|
|
8
|
+
const files = await discoverFiles({ workdir: context.workdir, include: rule.include, exclude: rule.exclude });
|
|
9
|
+
const findings = [];
|
|
10
|
+
const secretPattern = /(sk-[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16}|BEGIN (?:RSA |OPENSSH )?PRIVATE KEY)/;
|
|
11
|
+
for (const file of files) {
|
|
12
|
+
const lines = (await readWorkdirFile(context.workdir, file)).split('\n');
|
|
13
|
+
for (const [index, line] of lines.entries()) {
|
|
14
|
+
if (secretPattern.test(line)) {
|
|
15
|
+
findings.push(createFinding(rule, 'Potential secret token found', file, {
|
|
16
|
+
line: index + 1,
|
|
17
|
+
code: 'secret:token',
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return { findings, fixes: [] };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ResultFormatter, RuleEngineResult } from '../types';
|
|
2
|
+
/** JSON formatter for rule-engine results. */
|
|
3
|
+
export declare class JsonFormatter implements ResultFormatter {
|
|
4
|
+
constructor();
|
|
5
|
+
/** Format the full result as pretty JSON. */
|
|
6
|
+
format(result: RuleEngineResult): string;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=json.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/formatters/json.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAElE,8CAA8C;AAC9C,qBAAa,aAAc,YAAW,eAAe;;IAGjD,6CAA6C;IAC7C,MAAM,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM;CAG3C"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ResultFormatter, RuleEngineResult } from '../types';
|
|
2
|
+
/** Text formatter for human CLI output. */
|
|
3
|
+
export declare class TextFormatter implements ResultFormatter {
|
|
4
|
+
constructor();
|
|
5
|
+
/** Format findings as concise path-prefixed lines. */
|
|
6
|
+
format(result: RuleEngineResult): string;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/formatters/text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAElE,2CAA2C;AAC3C,qBAAa,aAAc,YAAW,eAAe;;IAGjD,sDAAsD;IACtD,MAAM,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM;CAY3C"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Text formatter for human CLI output. */
|
|
2
|
+
export class TextFormatter {
|
|
3
|
+
constructor() { }
|
|
4
|
+
/** Format findings as concise path-prefixed lines. */
|
|
5
|
+
format(result) {
|
|
6
|
+
if (result.findings.length === 0)
|
|
7
|
+
return 'No rule findings.';
|
|
8
|
+
return result.findings
|
|
9
|
+
.map((finding) => {
|
|
10
|
+
const location = finding.filePath === null
|
|
11
|
+
? '<workspace>'
|
|
12
|
+
: `${finding.filePath}${finding.line ? `:${finding.line}` : ''}`;
|
|
13
|
+
return `${finding.severity.toUpperCase()} ${finding.ruleId} ${location} ${finding.message}`;
|
|
14
|
+
})
|
|
15
|
+
.join('\n');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ProcessExecutor } from '@gobing-ai/ts-runtime';
|
|
2
|
+
import type { RuleEngineHost } from './rule-engine-host';
|
|
3
|
+
/** Register bundled evaluators and formatters on a host. */
|
|
4
|
+
export declare function registerBuiltins(host: RuleEngineHost, executor?: ProcessExecutor): void;
|
|
5
|
+
//# sourceMappingURL=builtins.d.ts.map
|
|
@@ -0,0 +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;AAS7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,4DAA4D;AAC5D,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,eAAe,GAAG,IAAI,CAavF"}
|